GDI + / C#:如何将图像另存为EMF?

时间:2020-03-06 14:55:08  来源:igfitidea点击:

如果使用Image.Save方法将图像保存到EMF / WMF,则会出现异常(http://msdn.microsoft.com/zh-cn/library/ktx83wah.aspx)

还有另一种将图像保存到EMF / WMF的方法吗?
有没有可用的编码器?

解决方案

图元文件是记录GDI操作序列的文件。它是可伸缩的,因为捕获了生成图片的原始操作序列,因此可以缩放所记录的坐标。

我认为,在.NET中,我们应该创建一个"元文件"对象,使用" Graphics.FromImage"创建一个" Graphics"对象,然后执行绘制步骤。绘制时会自动更新文件。我们可以在Graphics.AddMetafileComment的文档中找到一个小示例。

如果我们真的想将位图存储在图元文件中,请执行以下步骤,然后使用" Graphics.DrawImage"绘制位图。但是,缩放后将使用StretchBlt对其进行拉伸。

问题是:"还有另一种将图像保存到EMF / WMF的方法吗?"不是"什么是图元文件"或者"如何创建图元文件"或者"如何在图形中使用图元文件"。

我也在寻找"如何保存EMF / WMF"这个问题的答案
实际上,如果我们使用:

Graphics grfx = CreateGraphics();
  MemoryStream ms = new MemoryStream();
  IntPtr ipHdc = grfx.GetHdc();

  Metafile mf = new Metafile(ms, ipHdc);

  grfx.ReleaseHdc(ipHdc);
  grfx.Dispose();
  grfx = Graphics.FromImage(mf);

  grfx.FillEllipse(Brushes.Gray, 0, 0, 100, 100);
  grfx.DrawEllipse(Pens.Black, 0, 0, 100, 100);
  grfx.DrawArc(new Pen(Color.Red, 10), 20, 20, 60, 60, 30, 120);
  grfx.Dispose();

  mf.Save(@"C:\file.emf", ImageFormat.Emf);
  mf.Save(@"C:\file.png", ImageFormat.Png);

在这两种情况下,图像均保存为png格式。这是我无法解决的问题:/

如果我没记错的话,可以使用Metafile.GetHenhmetafile(),API GetEnhMetaFileBits()和Stream.Write()的组合来完成,例如

[DllImport("gdi32")] static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer);

IntPtr h = metafile.GetHenhMetafile();
int size = GetEnhMetaFileBits(h, 0, null);
byte[] data = new byte[size];
GetEnhMetaFileBits(h, size, data);
using (FileStream w = File.Create("out.emf")) {
    w.Write(data, 0, size);
}
// TODO: I don't remember whether the handle needs to be closed, but I guess not.

我认为这是解决问题时解决问题的方式。

erikkallen的答案是正确的。我从VB.NET尝试过此操作,不得不使用2种不同的DllImports使其起作用:

<System.Runtime.InteropServices.DllImportAttribute("gdi32.dll", EntryPoint:="GetEnhMetaFileBits")> _
    Public Shared Function GetEnhMetaFileBits(<System.Runtime.InteropServices.InAttribute()> ByVal hEMF As System.IntPtr, ByVal nSize As UInteger, ByVal lpData As IntPtr) As UInteger
End Function

    <System.Runtime.InteropServices.DllImportAttribute("gdi32.dll", EntryPoint:="GetEnhMetaFileBits")> _
    Public Shared Function GetEnhMetaFileBits(<System.Runtime.InteropServices.InAttribute()> ByVal hEMF As System.IntPtr, ByVal nSize As UInteger, ByVal lpData() As Byte) As UInteger
End Function

第一次导入用于第一次调用以获取emf大小。第二次导入以获取实际位。
或者,我们可以使用:

Dim h As IntPtr = mf.GetHenhmetafile()
CopyEnhMetaFileW(h, FileName)

这会将emf位直接复制到命名文件中。

"图像"是一个抽象类:我们要做什么取决于我们要处理的是"图元文件"还是"位图"。

使用" Metafile",使用GDI +创建图像并将其另存为EMF很简单。根据迈克的帖子:

var path = @"c:\foo.emf"
var g = CreateGraphics(); // get a graphics object from your form, or wherever
var img = new Metafile(path, g.GetHdc()); // file is created here
var ig = Graphics.FromImage(img);
// call drawing methods on ig, causing writes to the file
ig.Dispose(); img.Dispose(); g.ReleaseHdc(); g.Dispose();

这是我们大多数时候想要做的,因为这就是EMF的目的:以GDI +绘图命令的形式保存矢量图像。

我们可以使用上述方法并调用ig.DrawImage(your_bitmap)将Bitmap保存到EMF文件中,但是请注意,这不会将栅格数据秘密地转换为矢量图像。