Я использовал SharpDX для доступа к API WIC и Direct2d.Результаты впечатляют, если не сказать больше.При компоновке с Direct2d я вижу увеличение производительности на 400-500% по сравнению с GDI +.
Я также попробовал GDI + и Task Parallel Library, чтобы разбить изображения на четыре сектора и выполнить работу по компоновке в каждом ядре.Результаты оказались не такими значительными, как при использовании SharpDX.
Вот код, который я в итоге использовал.Ссылка на renderSettings - это просто объект конфигурации.Замените по мере необходимости вместе со списком изображений renderLayer.
/* SharpDX */
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.DXGI;
using SharpDX.IO;
using SharpDX.WIC;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using WicBitmap = SharpDX.WIC.Bitmap;
using D2DPixelFormat = SharpDX.Direct2D1.PixelFormat;
using WicPixelFormat = SharpDX.WIC.PixelFormat;
using Rectangle = System.Drawing.Rectangle;
using Bitmap = System.Drawing.Bitmap;
public Image FlattenImageDirect2d()
{
List<string> renderLayers = new List<string>()
{
"image1.jpg", "image1.png", "image2.png", "image3.png", "image4.png", "image5.png", "image6.png", "image7.png"
};
// Base image
string baseLayer = renderLayers[0];
// Create WIC and D2D factories
var wicFactory = new ImagingFactory();
var ddFactory = new SharpDX.Direct2D1.Factory();
// Get image size using WIC
int baseWidth, baseHeight;
using (var wicStream = new WICStream(wicFactory, renderDirectory + baseLayer, NativeFileAccess.Read)) {
var jpegDecoder = new JpegBitmapDecoder(wicFactory);
jpegDecoder.Initialize(wicStream, DecodeOptions.CacheOnDemand);
var frame = jpegDecoder.GetFrame(0);
baseWidth = frame.Size.Width;
baseHeight = frame.Size.Height;
frame.Dispose();
jpegDecoder.Dispose();
}
// Resize image?
bool resizeImage = (baseWidth != renderSettings.RenderWidth) || (baseHeight != renderSettings.RenderHeight);
// Bitmaps and render target settings
var wicBitmap = new WicBitmap(wicFactory, renderSettings.RenderWidth, renderSettings.RenderHeight, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);
var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new D2DPixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
var wicRenderTarget = new WicRenderTarget(ddFactory, wicBitmap, renderTargetProperties);
// Create bitmap render target used to draw all images to
SharpDX.Direct2D1.BitmapRenderTarget bitmapRenderTarget = new SharpDX.Direct2D1.BitmapRenderTarget(wicRenderTarget, CompatibleRenderTargetOptions.None, new D2DPixelFormat(Format.Unknown, AlphaMode.Premultiplied));
// Draw render layers
for (int i = 0; i < renderLayers.Count; i++) {
// First layer is always a jpeg, all other subsequent layers are png's
ImageFormat imageFormat = (i == 0) ? ImageFormat.Jpeg : ImageFormat.Png;
using (SharpDX.WIC.BitmapSource bitmapSource = LoadWicBitmap(wicFactory, renderDirectory + renderLayers[i], imageFormat, resizeImage, renderSettings.RenderWidth, renderSettings.RenderHeight)) {
// Convert WIC pixel format to D2D1 format
var formatConverter = new FormatConverter(wicFactory);
formatConverter.Initialize(bitmapSource, SharpDX.WIC.PixelFormat.Format32bppPBGRA, BitmapDitherType.None, null, 0f, BitmapPaletteType.MedianCut);
// Create direct 2d bitmap from wic bitmap
SharpDX.Direct2D1.Bitmap direct2DBitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(bitmapRenderTarget, formatConverter);
// Draw direct2d image to bitmap render target
wicRenderTarget.BeginDraw();
wicRenderTarget.DrawBitmap(direct2DBitmap, 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);
wicRenderTarget.EndDraw();
// Clean up
formatConverter.Dispose();
direct2DBitmap.Dispose();
}
}
// Final image data
byte[] imageData;
// Create streams to write output to.
using (var memoryStream = new MemoryStream()) {
using (var wicStream = new WICStream(wicFactory, memoryStream)) {
// Encode wic bitmap
var encoder = new JpegBitmapEncoder(wicFactory);
encoder.Initialize(wicStream);
var frameEncoder = new BitmapFrameEncode(encoder);
frameEncoder.Initialize();
frameEncoder.SetSize(renderSettings.RenderWidth, renderSettings.RenderHeight);
frameEncoder.PixelFormat = WicPixelFormat.FormatDontCare;
frameEncoder.WriteSource(wicBitmap);
frameEncoder.Commit();
encoder.Commit();
// Set image data
memoryStream.Position = 0;
imageData = memoryStream.ToArray();
// Clean up
frameEncoder.Dispose();
encoder.Dispose();
wicBitmap.Dispose();
wicRenderTarget.Dispose();
bitmapRenderTarget.Dispose();
ddFactory.Dispose();
wicFactory.Dispose();
frameEncoder = null;
encoder = null;
wicBitmap = null;
wicRenderTarget = null;
bitmapRenderTarget = null;
ddFactory = null;
wicFactory = null;
}
}
return Image.FromStream(new MemoryStream(imageData));
}
private BitmapSource LoadWicBitmap(ImagingFactory wicFactory, string path, ImageFormat imageFormat, bool resize, int resizeWidth = 0, int resizeHeight = 0)
{
PngBitmapDecoder pngDecoder;
JpegBitmapDecoder jpegDecoder;
BitmapFrameDecode bitmapFrameDecode;
var stream = new WICStream(wicFactory, path, NativeFileAccess.Read);
// Load the appropriate decoder
if (imageFormat == ImageFormat.Jpeg) {
jpegDecoder = new JpegBitmapDecoder(wicFactory);
jpegDecoder.Initialize(stream, DecodeOptions.CacheOnLoad);
bitmapFrameDecode = jpegDecoder.GetFrame(0);
jpegDecoder.Dispose();
}
else {
pngDecoder = new PngBitmapDecoder(wicFactory);
pngDecoder.Initialize(stream, DecodeOptions.CacheOnDemand);
bitmapFrameDecode = pngDecoder.GetFrame(0);
pngDecoder.Dispose();
}
// Clean up
stream.Dispose();
// Resize if necessary
if (resize) {
// Prepare scaler
var scaler = new BitmapScaler(wicFactory);
scaler.Initialize(bitmapFrameDecode, resizeWidth, resizeHeight, SharpDX.WIC.BitmapInterpolationMode.Fant);
return (BitmapSource)scaler;
}
return (BitmapSource)bitmapFrameDecode;
}