SharpDX:BitmapFrameEncode.WriteSource使用一些图像需要更长的时间

时间:2016-04-05 16:19:56

标签: c# gdi+ tiff sharpdx wic

我用SharpDX替换了一些狡猾的GDI +例程,从Stream加载一个双色调TIFF图像,在其上渲染文本,然后将其保存为TIFF格式作为Stream。

但SharpDX代码需要花费更长的时间来做同样的事情,我想知道我做错了什么。

从这里的示例中可以看出,我有两个不同的功能:

  • 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)接收现有的1000x500双色调TIFF图像用作基本图像,第二个(SaveRenderedImage)创建类似大小的WIC从头开始的位图。

执行现有图像的功能需要 30-40ms 才能执行,其中大部分时间(~30ms)由 BitmapFrameEncode.WriteSource 占用。此函数等效于替换的GDI +代码。

从头开始创建WicBitmap的函数需要 8-10ms 来执行,而不会在 BitmapFrameEncode.WriteSource 中花费大量时间,这与它取代的GDI +功能。唯一的区别是这个功能没有加载一个现有的图像,这就是我需要的。

与SaveRenderedImage相比,为什么 BitmapFrameEncode.WriteSource (它似乎是IWICBitmapFrameEncode的一个薄包装器)在BuildImageFromExistingImage中这么慢?

我的猜测是BuildImageFromExistingImage较慢,因为它对传入的图像进行了额外的转换(使用 FormatConverter ),将其转换为D2D将处理的像素格式,以及时间惩罚为此,在 BitmapFrameEncode.WriteSource 发生之前不会发挥作用。

我做错了吗?或者WIC(Windows Imaging Components)与基于GDI +的呼叫相比是否会变慢?

理想情况下,我需要第一种情况(BuildImageFromExistingImage)与它替换的GDI +代码一样快,并且期望 可以使其快速(如果不是更快),它旨在取代GDI +代码。

1 个答案:

答案 0 :(得分:-1)

在Windows 7及更高版本上,GDI +使用WIC编码器和解码器,因此实际编码步骤没有区别。如果你的两个方法产生相同的像素,它们将以相同的速度编码,并且该速度将与GDI +相同。

尽管WriteSource()似乎是您示例中的瓶颈,但这有点误导。 WIC使用延迟管道进行处理,这意味着您执行的所有步骤都会延迟,直到通过调用CopyPixels()请求像素为止。在您的情况下,由于您自己从不致电CopyPixels(),因此WriteSource()进行了此呼叫,并且当时执行了所有处理。

我对SharpDX并不熟悉,但我猜你可能想在创建用于绘图的WicBitmap对象时使用BitmapCreateCacheOption.CacheOnLoad。允许编码器实现位图意味着它将一次执行一条扫描线,这可能会对您的绘图性能产生负面影响。我相信CacheOnLoad选项会立即实现位图,因此它将在那时而不是在编码期间进行解码和转换。如果不出意外,这可能会帮助您找出瓶颈。

另外,我不知道这是否会导致性能问题,但BitmapPaletteType.MedianCut仅用于索引颜色像素类型。通常情况下,当您不需要量化时,可以使用BitmapPaletteType.Custom

最后,据我所知,Direct2D的规范像素格式为PixelFormat.Format32bppPBGRA。使用PRGBA变体可能会产生额外的开销。

请参阅here以获取与您类似的示例。