如何在C#/ WPF / WinForms中将WMF渲染到BitMap时启用消除锯齿功能?

时间:2016-06-22 12:24:27

标签: c# system.drawing metafile wmf .emf

为什么在执行此操作时线路等不会被消除锯齿?

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,如果有任何帮助的话。

1 个答案:

答案 0 :(得分:4)

这在使用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,那么您也可以考虑导出到XPS,WPF可以呈现抗锯齿。

如果无法在源代码转换,可以从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文件: WMF file with no anti-aliasing

在Paint中转换的EMF +文件: EMF+ file with anti-aliasing

为了完整起见,上述GdipConvertToEmfPlus方法是GDI +的“flat API”的一部分。它最初的目的是只提供GDI + C ++类。使用此方法的C ++ API称为Metafile.ConvertToEmfPlus