我用SharpDX替换了一些狡猾的GDI +例程,从Stream加载一个双色调TIFF图像,在其上渲染文本,然后将其保存为TIFF格式作为Stream。
但SharpDX代码需要花费更长的时间来做同样的事情,我想知道我做错了什么。
从这里的示例中可以看出,我有两个不同的功能:
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 +代码。
答案 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以获取与您类似的示例。