Я заменил некоторые изворотливые подпрограммы GDI + с помощью SharpDX для загрузки бинарного изображения TIFF из потока, визуализации текста на нем и сохранения его обратно в формате TIFF в виде потока.SharpDX: BitmapFrameEncode.WriteSource занимает гораздо больше времени с некоторыми изображениями
Но код SharpDX занимает гораздо больше времени, чтобы сделать то же самое, и мне интересно, не делаю ли я что-то неправильно.
Как видно из образца здесь, у меня есть 2 различные функции:
- RenderImageFromExistingImage
SaveRenderedImage
using System; using System.Diagnostics; using System.IO; using SharpDX; using SharpDX.Direct2D1; using SharpDX.DirectWrite; using SharpDX.DXGI; using SharpDX.WIC; using Factory = SharpDX.Direct2D1.Factory; using FactoryType = SharpDX.Direct2D1.FactoryType; using PixelFormat = SharpDX.WIC.PixelFormat; using WicBitmap = SharpDX.WIC.Bitmap; public class ImageCreator2 { private static ImagingFactory _wicFactory; private static Factory _d2DFactory; private static SharpDX.DirectWrite.Factory _dwFactory; private int _imageWidth = 1000, _imageHeight = 500; private readonly int _imageDpi = 96; public ImageCreator2() { _wicFactory = new ImagingFactory(); _d2DFactory = new Factory(FactoryType.SingleThreaded); _dwFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Shared); } private void RenderImage(WicRenderTarget renderTarget) { using (var blackBrush = new SolidColorBrush(renderTarget, Color4.Black)) using (var tformat = new TextFormat(_dwFactory, "Arial", 30f)) using (var tformat2 = new TextFormat(_dwFactory, "Arial", 11f)) { renderTarget.BeginDraw(); renderTarget.Clear(Color.White); renderTarget.DrawText("TEST", tformat, new RectangleF(300f, 30f, 100f, 20f), blackBrush); renderTarget.DrawText("MORE TEST", tformat2, new RectangleF(30f, 150f, 100f, 20f), blackBrush); renderTarget.DrawLine(new Vector2(0f, 25f), new Vector2(500f, 25f), blackBrush); renderTarget.DrawLine(new Vector2(0f, 210f), new Vector2(500f, 210f), blackBrush); renderTarget.EndDraw(); } } public void BuildImageFromExistingImage(byte[] image, Stream systemStream) { using (var checkStream = new MemoryStream(image)) using ( var inDecoder = new BitmapDecoder(_wicFactory, checkStream, DecodeOptions.CacheOnDemand)) using (var converter = new FormatConverter(_wicFactory)) { if (inDecoder.FrameCount > 0) { using (var frame = inDecoder.GetFrame(0)) { converter.Initialize(frame, PixelFormat.Format32bppPRGBA, BitmapDitherType.None, null, 0.0f, BitmapPaletteType.MedianCut); _imageWidth = converter.Size.Width; _imageHeight = converter.Size.Height; } } else { throw new Exception(); } var renderProperties = new RenderTargetProperties( RenderTargetType.Software, new SharpDX.Direct2D1.PixelFormat(Format.Unknown, AlphaMode.Unknown), _imageDpi, _imageDpi, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT); using (var wicBitmap = new WicBitmap( _wicFactory, converter, BitmapCreateCacheOption.CacheOnDemand)) using ( var renderTarget = new WicRenderTarget(_d2DFactory, wicBitmap, renderProperties)) { RenderImage(renderTarget); using ( var encoder = new BitmapEncoder(_wicFactory, ContainerFormatGuids.Tiff)) { encoder.Initialize(systemStream); using (var bitmapFrameEncode = new BitmapFrameEncode(encoder)) { var pixFormat = PixelFormat.Format32bppPRGBA; bitmapFrameEncode.Initialize(); bitmapFrameEncode.SetSize(_imageWidth, _imageHeight); bitmapFrameEncode.SetResolution(96, 96); bitmapFrameEncode.SetPixelFormat(ref pixFormat); //This takes 30-40ms per image. var watch = new Stopwatch(); try { watch.Start(); bitmapFrameEncode.WriteSource(wicBitmap); } finally { watch.Stop(); } Console.WriteLine("Saved real image in {0} ms.", watch.Elapsed.TotalMilliseconds); bitmapFrameEncode.Commit(); } encoder.Commit(); } } } } public void SaveRenderedImage(Stream systemStream) { var renderProperties = new RenderTargetProperties( RenderTargetType.Default, new SharpDX.Direct2D1.PixelFormat(Format.Unknown, AlphaMode.Unknown), _imageDpi, _imageDpi, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT); using (var wicBitmap = new WicBitmap( _wicFactory, _imageWidth, _imageHeight, PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnDemand )) using (var renderTarget = new WicRenderTarget(_d2DFactory, wicBitmap, renderProperties)) { RenderImage(renderTarget); using ( var encoder = new BitmapEncoder(_wicFactory, ContainerFormatGuids.Tiff)) { encoder.Initialize(systemStream); using (var bitmapFrameEncode = new BitmapFrameEncode(encoder)) { bitmapFrameEncode.Initialize(); bitmapFrameEncode.SetSize(_imageWidth, _imageHeight); bitmapFrameEncode.SetResolution(_imageDpi, _imageDpi); //This takes 8-10ms per image. var watch = new Stopwatch(); try { watch.Start(); bitmapFrameEncode.WriteSource(wicBitmap); } finally { watch.Stop(); } Console.WriteLine("Saved generated image in {0} ms.", watch.Elapsed.TotalMilliseconds); bitmapFrameEncode.Commit(); } encoder.Commit(); } } } }
Они в основном идентичны, и сделать примерно то же самое, кроме первого (RenderImageFromExistingImage) принимает существующий бинарный TIFF 1000x500 для использования как базовое изображение, а второе (SaveRenderedImage) создает растровое изображение WIC аналогичного размера с нуля.
Функция, которая принимает существующий файл занимает около 30-40ms выполнить, причем основная часть этого времени (~ 30ms), занимаемого BitmapFrameEncode.WriteSource. Эта функция эквивалентна заменяемому коду GDI +.
Функция, которая создает WicBitmap с нуля занимает 8-10ms выполнить, не занимая много времени в BitmapFrameEncode.WriteSource, что примерно такое же количество времени, как GDI + функции, которую она заменила. Единственное различие заключается в том, что эта функция не загружает предварительное изображение, которое мне нужно.
Почему BitmapFrameEncode.WriteSource (который, как представляется, тонкая оболочка вокруг IWICBitmapFrameEncode) так медленно BuildImageFromExistingImage, по сравнению с SaveRenderedImage?
Я думаю, что BuildImageFromExistingImage медленнее, потому что она делает дополнительную конверсию на подводящем изображения (с помощью FormatConverter), чтобы преобразовать его в формат пикселя, который D2D будет обрабатывать, и что штрафное время для этого делает не вступают в игру до BitmapFrameEncode.WriteSource происходит.
Есть ли что-то, что я делаю неправильно? Или WIC (компоненты обработки изображений Windows) просто медленны по сравнению с вызовами на основе GDI +?
В идеале мне нужен первый случай (BuildImageFromExistingImage), чтобы быть столь же быстро, как GDI + кода он заменил, и следовало ожидать, что должно можно сделать это так быстро, если не быстрее, чем GDI + кода оно предназначенный для замены.
Спасибо за советы и объяснения, я как-то понял, что 'WriteSource()' именно там, где все действительно произошло. 'CacheOnLoad' vs' CacheOnDemand' не имеет заметной разницы, также не 'Format32bppPBGRA' и' Format32bppPRGBA'. Я попробую 'BitmapPaletteType.Custom' и посмотрю, какие результаты я получу. –
Заставляет ли 'CacheOnLoad' менять время? Я бы не ожидал, что это уменьшит общее время выполнения, но это должно повлиять на вызов «WriteSource». Кроме того, вы можете проверить значение 'pixFormat' после вашего вызова' bitmapFrameEncode.SetPixelFormat (ref pixFormat) '. Если формат входного изображения не поддерживается кодировщиком, он выполнит преобразование во время сохранения и сообщит вам формат, в который он будет преобразовываться. Это скажет вам, несет ли сама по себе стоимость конверсии. – saucecontrol