如何在 C#/WPF/WinForms 中将 WMF 渲染为 BitMap 时启用抗锯齿?
How to enable anti-aliasing when rendering WMF to BitMap in C#/WPF/WinForms?
为什么在执行此操作时线条等不会消除锯齿?
using (var myGraphics = Graphics.FromImage(bitmap))
{
myGraphics.CompositingQuality = CompositingQuality.HighQuality;
myGraphics.SmoothingMode = SmoothingMode.HighQuality;
myGraphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
myGraphics.Clear(backgroundColor);
myGraphics.EnumerateMetafile(m_metafile, new Point(0, 0), m_metafileDelegate);
}
委托函数如下所示:
private bool MetafileCallback(EmfPlusRecordType recordType, int flags, int dataSize, IntPtr data, PlayRecordCallback callbackData)
{
byte[] dataArray = null;
if (data != IntPtr.Zero)
{
// Copy the unmanaged record to a managed byte buffer
// that can be used by PlayRecord.
dataArray = new byte[dataSize];
Marshal.Copy(data, dataArray, 0, dataSize);
}
m_metafile.PlayRecord(recordType, flags, dataSize, dataArray);
return true;
}
我是否需要覆盖特定类型的 PlayRecord 才能在此处获得抗锯齿功能?
WMF 来自 AutoCAD,如果有帮助的话。
这在使用 WMF 图元文件的 GDI+ 中是不可能的,但在 EMF Plus 中是可行的。您可以在源代码处转换为 EMF Plus,或者使用记录不完整的 GDI+ 方法(见下文)即时转换。
GDI(不是 GDI+)渲染 WMF 文件时不使用其下的 GDI+ Graphics 对象的任何合成,它只是直接 GDI 调用的枚举。 See this question for more, but all answers say about the same thing.
如果您可以将文件转换为 EMF Plus,这将使用 GDI+ 方法呈现内容,并使用包括抗锯齿在内的 GDI+ 合成。如果您已经在使用 WPF,您还可以考虑导出到 WPF 可以呈现抗锯齿的 XPS。
如果不能在源头转换,可以从C#调用GDI+方法,但不够优雅。您需要有权访问 System.Drawing 类:
使用的本机句柄
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
internal static extern int GdipConvertToEmfPlus(HandleRef graphics,
HandleRef metafile,
out Boolean conversionSuccess,
EmfType emfType,
[MarshalAsAttribute(UnmanagedType.LPWStr)]
String description,
out IntPtr convertedMetafile);
您可以将其与类似于以下的代码一起使用:
using (var graphics = Graphics.FromImage(bmp))
using (var metafile = Metafile.FromFile(@"drawing.wmf"))
using (var imageAttr = new ImageAttributes())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
var metafileHandleField = typeof(Metafile).GetField("nativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
var imageAttributesHandleField = typeof(ImageAttributes).GetField("nativeImageAttributes", BindingFlags.Instance | BindingFlags.NonPublic);
var graphicsHandleProperty = typeof(Graphics).GetProperty("NativeGraphics", BindingFlags.Instance | BindingFlags.NonPublic);
var setNativeImage = typeof(Image).GetMethod("SetNativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
IntPtr mf = (IntPtr)metafileHandleField.GetValue(metafile);
IntPtr ia = (IntPtr)imageAttributesHandleField.GetValue(imageAttr);
IntPtr g = (IntPtr)graphicsHandleProperty.GetValue(graphics);
Boolean isSuccess;
IntPtr emfPlusHandle;
var status = GdipConvertToEmfPlus(new HandleRef(graphics, g),
new HandleRef(metafile, mf),
out isSuccess,
EmfType.EmfPlusOnly,
"",
out emfPlusHandle);
if (status != 0)
{
throw new Exception("Can't convert");
}
using (var emfPlus = (Metafile)System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(typeof(Metafile)))
{
setNativeImage.Invoke(emfPlus, new object[] { emfPlusHandle });
// use EnumerateMetafile on emfPlus as per your example code or save it:
emfPlus.Save(@"drawing.emf");
}
}
Here's a working example for LinqPad。它将 WMF 文件 (drawing.wmf) 转换为 EMF Plus 图元文件,并将其显示在结果面板中。
Paint 中的 WMF 文件:
在 Paint 中转换的 EMF+ 文件:
为了完整起见,上述 GdipConvertToEmfPlus
方法是所谓的“flat API" of GDI+. Its original purpose was to serve only the GDI+ C++ classes. The C++ API which uses this method is called Metafile.ConvertToEmfPlus
.
方法的一部分
为什么在执行此操作时线条等不会消除锯齿?
using (var myGraphics = Graphics.FromImage(bitmap))
{
myGraphics.CompositingQuality = CompositingQuality.HighQuality;
myGraphics.SmoothingMode = SmoothingMode.HighQuality;
myGraphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
myGraphics.Clear(backgroundColor);
myGraphics.EnumerateMetafile(m_metafile, new Point(0, 0), m_metafileDelegate);
}
委托函数如下所示:
private bool MetafileCallback(EmfPlusRecordType recordType, int flags, int dataSize, IntPtr data, PlayRecordCallback callbackData)
{
byte[] dataArray = null;
if (data != IntPtr.Zero)
{
// Copy the unmanaged record to a managed byte buffer
// that can be used by PlayRecord.
dataArray = new byte[dataSize];
Marshal.Copy(data, dataArray, 0, dataSize);
}
m_metafile.PlayRecord(recordType, flags, dataSize, dataArray);
return true;
}
我是否需要覆盖特定类型的 PlayRecord 才能在此处获得抗锯齿功能?
WMF 来自 AutoCAD,如果有帮助的话。
这在使用 WMF 图元文件的 GDI+ 中是不可能的,但在 EMF Plus 中是可行的。您可以在源代码处转换为 EMF Plus,或者使用记录不完整的 GDI+ 方法(见下文)即时转换。
GDI(不是 GDI+)渲染 WMF 文件时不使用其下的 GDI+ Graphics 对象的任何合成,它只是直接 GDI 调用的枚举。 See this question for more, but all answers say about the same thing.
如果您可以将文件转换为 EMF Plus,这将使用 GDI+ 方法呈现内容,并使用包括抗锯齿在内的 GDI+ 合成。如果您已经在使用 WPF,您还可以考虑导出到 WPF 可以呈现抗锯齿的 XPS。
如果不能在源头转换,可以从C#调用GDI+方法,但不够优雅。您需要有权访问 System.Drawing 类:
使用的本机句柄[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
internal static extern int GdipConvertToEmfPlus(HandleRef graphics,
HandleRef metafile,
out Boolean conversionSuccess,
EmfType emfType,
[MarshalAsAttribute(UnmanagedType.LPWStr)]
String description,
out IntPtr convertedMetafile);
您可以将其与类似于以下的代码一起使用:
using (var graphics = Graphics.FromImage(bmp))
using (var metafile = Metafile.FromFile(@"drawing.wmf"))
using (var imageAttr = new ImageAttributes())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
var metafileHandleField = typeof(Metafile).GetField("nativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
var imageAttributesHandleField = typeof(ImageAttributes).GetField("nativeImageAttributes", BindingFlags.Instance | BindingFlags.NonPublic);
var graphicsHandleProperty = typeof(Graphics).GetProperty("NativeGraphics", BindingFlags.Instance | BindingFlags.NonPublic);
var setNativeImage = typeof(Image).GetMethod("SetNativeImage", BindingFlags.Instance | BindingFlags.NonPublic);
IntPtr mf = (IntPtr)metafileHandleField.GetValue(metafile);
IntPtr ia = (IntPtr)imageAttributesHandleField.GetValue(imageAttr);
IntPtr g = (IntPtr)graphicsHandleProperty.GetValue(graphics);
Boolean isSuccess;
IntPtr emfPlusHandle;
var status = GdipConvertToEmfPlus(new HandleRef(graphics, g),
new HandleRef(metafile, mf),
out isSuccess,
EmfType.EmfPlusOnly,
"",
out emfPlusHandle);
if (status != 0)
{
throw new Exception("Can't convert");
}
using (var emfPlus = (Metafile)System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(typeof(Metafile)))
{
setNativeImage.Invoke(emfPlus, new object[] { emfPlusHandle });
// use EnumerateMetafile on emfPlus as per your example code or save it:
emfPlus.Save(@"drawing.emf");
}
}
Here's a working example for LinqPad。它将 WMF 文件 (drawing.wmf) 转换为 EMF Plus 图元文件,并将其显示在结果面板中。
Paint 中的 WMF 文件:
在 Paint 中转换的 EMF+ 文件:
为了完整起见,上述 GdipConvertToEmfPlus
方法是所谓的“flat API" of GDI+. Its original purpose was to serve only the GDI+ C++ classes. The C++ API which uses this method is called Metafile.ConvertToEmfPlus
.