2016-06-22 6 views
4

Почему при выполнении этого не происходит сглаживание линий и т. Д.Как включить сглаживание при рендеринге WMF в BitMap в 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, если это поможет.

+0

Как это связано с WPF? 'Graphics' - это' System.Drawing', 'Metafile' - это' System.Drawing.Image', оба пространства имен WinForms. – Clemens

+0

Я использую его в WPF-приложении, но да, возможно, не связан. Я обновлю теги. – Macke

ответ

3

Это невозможно в GDI + с использованием метафайла WMF, но с 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 + compositing, включая сглаживание. Если вы уже используете WPF, вы также можете рассмотреть возможность экспорта в XPS, который WPF может отображать сглаженными.

Если вы не можете преобразовать исходный код, вы можете вызвать метод GDI + с C#, но он не изящный. Вы должны иметь доступ к нативным ручкам, используемых классы 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 и отображает его на панели результатов.

WMF файл в Paint: WMF file with no anti-aliasing

Старинное EMF + файл в Paint: EMF+ file with anti-aliasing


Для полноты картины выше GdipConvertToEmfPlus метод является частью того, что известно как "flat API "GDI +. Его первоначальная цель состояла в том, чтобы обслуживать только классы GDI + C++. C++ API, который использует этот метод, называется Metafile.ConvertToEmfPlus.

+0

Спасибо! Я не могу много сделать для источника WMF (AutoCAD LT), поэтому в этом случае немного естественности. Я дам ему попробовать и вернусь, если он сработает, и он выглядит так, как должен (учитывая мой ограниченный опыт работы с WMF/EMF/GDI +). (Я полагал, что функции GDI будут поддерживать некоторый глобальный флаг «AntiAlias», и я только что пропустил какой). – Macke

+0

Я не добираюсь туда. (это, g) должно быть (графика, g), правильно? Затем я получаю isSuccess = true, но конструктор Metafile терпит неудачу с «общей ошибкой System.Runtime.InteropServices.ExternalException (0x80004005): общая ошибка произошла в GDI +». Кроме того, imageAttr, похоже, не используется вообще? – Macke

+0

Вы правы, код не готов к использованию и предполагает, что у вас может быть ImageAttributes, которого вы не можете. У вас есть WMF, который вы можете поделиться, чтобы воспроизвести проблему? – codekaizen