我正在尝试将 Drawing.Bitmap 转换为 Imaging.Metafile ,以便将元文件插入 Forms.RichTextBox 。 (作为引用,在元文件中嵌入位图是将位图放入富文本的推荐做法(请参阅富文本格式 (RTF) 规范版本 1.9.1,第 149 页。)不幸的是,这似乎也是嵌入的唯一方法将图像插入 Forms.RichTextBox,因为我无法获得将位图插入 RichTextBox 的任何设备相关或设备无关的方法来工作。)

稍后,我必须从元文件中检索像素数据。我要求图元文件的像素与位图的像素完全匹配。然而,当我执行转换时,像素会略有改变。 (也许是因为 GDI 图像色彩管理 (ICM)?)

这是我的技术:

public static Imaging.Metafile BitmapToMetafileViaGraphicsDrawImage(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap)
{
    Imaging.Metafile metafile;
    using (IO.MemoryStream stream = new IO.MemoryStream())
    using (Drawing.Graphics rtfBoxGraphics = rtfBox.CreateGraphics())
    {
        IntPtr pDeviceContext = rtfBoxGraphics.GetHdc();

        metafile = new Imaging.Metafile(stream, pDeviceContext);
        using (Drawing.Graphics imageGraphics = Drawing.Graphics.FromImage(metafile))
        {
            //imageGraphics.DrawImage(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
            imageGraphics.DrawImageUnscaled(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
        }
        rtfBoxGraphics.ReleaseHdc(pDeviceContext);
    }
    return metafile;
}

在这种情况下,我以这种方式访问​​图元文件的像素:
metafile.Save(stream, Imaging.ImageFormat.Png);
Bitmap bitmap = new Bitmap(stream, false);
bitmap.GetPixel(x, y);

我也尝试使用 BitBlt 技术但没有成功。

BitBlt 技术:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
static extern int BitBlt(
  IntPtr hdcDest,     // handle to destination DC (device context)
  int nXDest,         // x-coord of destination upper-left corner
  int nYDest,         // y-coord of destination upper-left corner
  int nWidth,         // width of destination rectangle
  int nHeight,        // height of destination rectangle
  IntPtr hdcSrc,      // handle to source DC
  int nXSrc,          // x-coordinate of source upper-left corner
  int nYSrc,          // y-coordinate of source upper-left corner
  System.Int32 dwRop  // raster operation code
  );

public static Imaging.Metafile BitmapToMetafileViaBitBlt(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap)
{
    const int SrcCopy = 0xcc0020;

    Graphics bitmapGraphics = Graphics.FromImage(bitmap);
    IntPtr pBitmapDeviceContext = bitmapGraphics.GetHdc();

    RectangleF rect = new RectangleF(new PointF(0, 0), new SizeF(bitmap.Width, bitmap.Height));
    Imaging.Metafile metafile = new Imaging.Metafile(pBitmapDeviceContext, rect);
    Graphics metafileGraphics = Graphics.FromImage(metafile);
    IntPtr metafileDeviceContext = metafileGraphics.GetHdc();

    BitBlt(pBitmapDeviceContext, 0, 0, bitmap.Width, bitmap.Height,
        metafileDeviceContext, 0, 0, SrcCopy);

    return metafile;
}

我什至不确定这种技术是否正确复制了像素数据。当我稍后尝试访问元文件中的数据时,此技术失败:
IntPtr h = metafile.GetHenhmetafile(); // ArgumentException "Parameter is not valid."
byte[] data;
uint size = GetEnhMetaFileBits(h, 0, out data);
data = new byte[size];
GetEnhMetaFileBits(h, size, out data);
stream = new IO.MemoryStream(data);

如何在不改变像素的情况下将位图转换为图元文件,然后稍后再次检索像素数据?谢谢!

设置位图分辨率

这是我尝试设置位图分辨率以使图元文件的分辨率匹配的方式:
Drawing.Bitmap bitmap = new Drawing.Bitmap(width, height,
Imaging.PixelFormat.Format32bppArgb); // Use 32-bit pixels so that each component (ARGB) matches up with a byte
// Try setting the resolution to see if that helps with conversion to/from metafiles
Drawing.Graphics rtfGraphics = rtfBox.CreateGraphics();
bitmap.SetResolution(rtfGraphics.DpiX, rtfGraphics.DpiY);

// Set the pixel data
...

// Return the bitmap
return bitmap;

rtfBox 与发送到 BitmapToMetafileViaGraphicsDrawImage 的相同。

最佳答案

您可以手动创建和处理图元文件(例如,枚举其记录),在这种情况下,您实际上可以将带有其确切数据的位图插入到图元文件流中。然而,这并不像听起来那么简单。

在播放 GDI 图元文件时,所有操作都在内部转换为 GDI+,这实际上是一个完全不同的 API,它对很多事情的处理方式也不同。不幸的是,一旦图元文件有一些复杂性或需要转换输出,内置的方法就是让 GDI 在低分辨率位图上渲染图元文件,然后绘制它,这永远不会给你你想要的结果正在寻找。

对于一个项目(封闭源代码 - 所以不要费心询问源代码;))我必须实现一个完整的 GDI-to-GDI+ 播放引擎,类似于 EMFExplorer 的功能,但仅使用托管代码和单个字符重新-对齐精确的文本输出。这只是说它可以完成,如果您愿意投入一些时间来处理您自己需要的所有元文件记录(这应该只是一个小子集,但仍然如此)。

编辑以解决评论中提出的问题:

图元文件只不过是一系列绘图指令,基本上是执行 GDI(+) 操作的记录。因此,您应该能够将元文件构建为基本上仅由标题和位图组成的二进制数据,在这种情况下(因为您不通过 drwing 操作但始终在二进制级别处理位图)您将能够从您写入的元文件中检索完全相同的数据。

幸运的是,几年以来,Microsoft 一直在记录并向公众提供他们的文件格式和协议(protocol),因此可以使用 WMF/EMF 文件格式的精确文档。 MS 在这里解释了元文件格式:
http://msdn.microsoft.com/en-us/library/cc230514(PROT.10).aspx

它的结构概述如下:
http://msdn.microsoft.com/en-us/library/cc230516(v=PROT.10).aspx

位图记录描述如下:
http://msdn.microsoft.com/en-us/library/cc231160(v=PROT.10).aspx

使用此信息,您应该能够将二进制数据放在一起(可能使用 BinaryWriter ),然后还加载/枚举文件以取回数据(例如,再次在元文件中查找位图并从中提取所需的数据)。

关于c# - 将 Drawing.Bitmap 转换为 Imaging.Metafile - Pixel Perfect,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3846448/

10-17 00:49