C# 调整图像大小-WIC与GDI
编辑:我发现这两种实现都不适合服务器端场景:微软不支持它们。所以不要用它们 因此,我在下面提供了两个独立的实现。我想我已经完成了Windows Imaging Component(WIC)的实现 一些评论:C# 调整图像大小-WIC与GDI,c#,image,gdi,wic,C#,Image,Gdi,Wic,编辑:我发现这两种实现都不适合服务器端场景:微软不支持它们。所以不要用它们 因此,我在下面提供了两个独立的实现。我想我已经完成了Windows Imaging Component(WIC)的实现 一些评论: GDI的实现似乎比WIC更快(WIC@0.26s/photo,GDI@0.14s/photo) 当多线程GDI降至~0.10s/photo时,WIC实现没有看到任何性能提升 服务器端处理只支持WIC,但是如果它不支持多线程,那么它就不能很好地扩展 在i7上运行时,所讨论的照片是由奥林巴斯数
- GDI的实现似乎比WIC更快(WIC@0.26s/photo,GDI@0.14s/photo)
- 当多线程GDI降至~0.10s/photo时,WIC实现没有看到任何性能提升
- 服务器端处理只支持WIC,但是如果它不支持多线程,那么它就不能很好地扩展
- 在i7上运行时,所讨论的照片是由奥林巴斯数码相机创建的典型1.2MB图像
- 我从中得到灵感
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Mrwa.Bms.Common.Imaging
{
/// <summary>
/// Generates JPEG image previews for any supplied .NET image supported files
/// </summary>
/// <remarks>
/// WIC = Windows Imaging Component
/// http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx
/// </remarks>
public class WicImagePreviewGenerator : IImagePreviewGenerator
{
private const int ScreenDpi = 96;
private BitmapFrame _imageFrame;
public WicImagePreviewGenerator(Stream stream)
{
Contract.Requires(stream != null);
try
{
if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
_imageFrame = decoder.Frames[0];
}
catch (NotSupportedException ex)
{
throw new ArgumentException("The image is corrupt.", "stream", ex);
}
}
public ImagePreviewGeneratorDto Generate(
int pixelSize, int jpegQuality = 80, int dpi = 72,
ImagePreviewGeneratorResizeQualityEnum resizeQuality = ImagePreviewGeneratorResizeQualityEnum.HighQuality)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
// create a new target drawing canvas
var width = (int) (previewWidth*(ScreenDpi/(decimal) dpi));
var height = (int) (previewHeight*(ScreenDpi/(decimal) dpi));
var drawing = new ImageDrawing(
_imageFrame,
new Rect(0, 0, width, height));
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, GetScalingMode(resizeQuality));
group.Children.Add(drawing);
// generate the preview image frame
BitmapFrame previewFrame;
var previewVisual = new DrawingVisual();
using (var previewContext = previewVisual.RenderOpen())
{
previewContext.DrawDrawing(group);
previewContext.Close();
var previewBitmap = new RenderTargetBitmap(
previewWidth, previewHeight,
dpi, dpi,
PixelFormats.Default);
previewBitmap.Render(previewVisual);
previewFrame = BitmapFrame.Create(previewBitmap);
}
// generate the result as a JPG
using (var content = new MemoryStream())
{
var previewEncoder = new JpegBitmapEncoder { QualityLevel = jpegQuality };
previewEncoder.Frames.Add(previewFrame);
previewEncoder.Save(content);
content.Flush();
return new ImagePreviewGeneratorDto
{
Preview = content.ToArray(),
Width = previewWidth,
Height = previewHeight
};
}
}
// not used - retained for reference only
public IEnumerable<byte> GenerateOptimised(int pixelSize, int jpegQuality = 80)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
var transform = new TransformedBitmap(
_imageFrame, new ScaleTransform(previewWidth, previewHeight, 0, 0));
var previewFrame = BitmapFrame.Create(transform);
// generate the result as a JPG
using (var result = new MemoryStream())
{
var previewEncoder = new JpegBitmapEncoder { QualityLevel = jpegQuality };
previewEncoder.Frames.Add(previewFrame);
previewEncoder.Save(result);
return result.ToArray();
}
}
private static BitmapScalingMode GetScalingMode(ImagePreviewGeneratorResizeQualityEnum previewQuality)
{
switch (previewQuality)
{
case ImagePreviewGeneratorResizeQualityEnum.HighQuality:
return BitmapScalingMode.HighQuality;
case ImagePreviewGeneratorResizeQualityEnum.HighSpeed:
return BitmapScalingMode.LowQuality;
default:
throw new NotSupportedException("Invalid preview quality specified.");
}
}
private void CalculateDimensions(int pixelSize, out int width, out int height)
{
var originalWidth = _imageFrame.PixelWidth;
var originalHeight = _imageFrame.PixelHeight;
// scale: reduce the longest side down to 'X' pixels and maintain the aspect ratio
if (originalWidth <= pixelSize && originalHeight <= pixelSize)
{
width = originalWidth;
height = originalHeight;
}
else if (originalWidth >= originalHeight)
{
width = pixelSize;
height = (int)((pixelSize / (decimal)originalWidth) * originalHeight);
}
else
{
width = (int)((pixelSize / (decimal)originalHeight) * originalWidth);
height = pixelSize;
}
}
#region IDisposable
private bool _disposed;
~WicImagePreviewGenerator()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// free managed resources
_imageFrame = null;
}
// free unmanaged resources
_disposed = true;
}
#endregion
}
}
using System;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
namespace Mrwa.Bms.Common.Imaging
{
/// <summary>
/// Generates JPEG image previews for any supplied .NET image supported files
/// </summary>
/// <remarks>
/// Feel free to use this Client side. Not officially supported for back-end scenarios.
/// </remarks>
public class GdiPlusImagePreviewGenerator : IImagePreviewGenerator
{
private Image _image;
public GdiPlusImagePreviewGenerator(Stream stream)
{
Contract.Requires(stream != null);
try
{
if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
_image = Image.FromStream(stream);
}
catch (ArgumentException ex)
{
throw new ArgumentException("The image is corrupt.", "stream", ex);
}
}
private void CalculateDimensions(int pixelSize, out int width, out int height)
{
var originalWidth = _image.Width;
var originalHeight = _image.Height;
// scale: reduce the longest side down to 'X' pixels and maintain the aspect ratio
if (originalWidth <= pixelSize && originalHeight <= pixelSize)
{
width = originalWidth;
height = originalHeight;
}
else if (originalWidth >= originalHeight)
{
width = pixelSize;
height = (int)((pixelSize / (decimal)originalWidth) * originalHeight);
}
else
{
width = (int)((pixelSize / (decimal)originalHeight) * originalWidth);
height = pixelSize;
}
}
/// <remarks>
/// Not changing the colour depth; apparently the conversion can be quite poor
/// Don't forget to dispose of the stream
/// </remarks>
public ImagePreviewGeneratorDto Generate(
int pixelSize, int jpegQuality = 80, int dpi = 72,
ImagePreviewGeneratorResizeQualityEnum resizeQuality = ImagePreviewGeneratorResizeQualityEnum.HighQuality)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
// resize the image (in terms of pixels) and standardise the DPI
using (var previewImage = new Bitmap(previewWidth, previewHeight))
{
previewImage.SetResolution(dpi, dpi);
using (var graphics = Graphics.FromImage(previewImage))
{
switch (resizeQuality)
{
case ImagePreviewGeneratorResizeQualityEnum.HighQuality:
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
break;
case ImagePreviewGeneratorResizeQualityEnum.HighSpeed:
graphics.SmoothingMode = SmoothingMode.HighSpeed;
graphics.InterpolationMode = InterpolationMode.Low;
graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
break;
default:
throw new NotSupportedException("Invalid Preview Quality Enum supplied.");
}
graphics.DrawImage(_image, new Rectangle(0, 0, previewWidth, previewHeight));
}
// convert to a JPG and reduce the quality
using (var content = new MemoryStream())
{
var jpegEncoder = GetEncoder(ImageFormat.Jpeg);
previewImage.Save(content, jpegEncoder,
new EncoderParameters
{
Param = new[] { new EncoderParameter(Encoder.Quality, jpegQuality) },
});
content.Flush();
// return the stream
return new ImagePreviewGeneratorDto
{
Preview = content.ToArray(),
Width = previewWidth,
Height = previewHeight
};
}
}
}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
return codecs.FirstOrDefault(codec => codec.FormatID == format.Guid);
}
#region IDisposable
private bool _disposed;
~GdiPlusImagePreviewGenerator()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// free managed resources
if (_image != null)
{
_image.Dispose();
_image = null;
}
}
// free unmanaged resources
_disposed = true;
}
#endregion
}
}
使用系统;
使用System.Collections.Generic;
使用System.Diagnostics.Contracts;
使用System.IO;
使用System.Windows;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
名称空间Mrwa.Bms.Common.Imaging
{
///
///为任何提供的.NET图像支持文件生成JPEG图像预览
///
///
///WIC=Windows映像组件
/// http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx
///
公共类WicImagePreviewGenerator:IImagePreviewGenerator
{
私有常量int ScreenDpi=96;
私有位图帧imageFrame;
公共WicImagePreviewGenerator(流)
{
Contract.Requires(流!=null);
尝试
{
if(stream.CanSeek)stream.Seek(0,SeekOrigin.Begin);
var decoder=BitmapDecoder.Create(流,BitmapCreateOptions.PreservePixelFormat,BitmapCacheOption.Default);
_imageFrame=解码器。帧[0];
}
捕获(不支持例外)
{
抛出新的ArgumentException(“图像已损坏。”,“流”,ex);
}
}
要生成的公共图像预览生成器(
int pixelSize,int jpegQuality=80,int dpi=72,
ImagePreviewGeneratorResizeQualityEnum resizeQuality=ImagePreviewGeneratorResizeQualityEnum.HighQuality)
{
整数预览宽度;
国际预演;
计算尺寸(像素大小、外预览宽度、外预览宽度);
//创建新的目标图形画布
变量宽度=(int)(预览宽度*(屏幕dpi/(十进制)dpi));
变量高度=(int)(预览视图*(屏幕dpi/(十进制)dpi));
var绘图=新图像绘图(
_图像框,
新的矩形(0,0,宽度,高度);
var group=新绘图组();
SetBitmapScalingMode(组,GetScalingMode(resizeQuality));
组。子组。添加(绘图);
//生成预览图像帧
位图帧预览帧;
var previewVisual=新建DrawingVisual();
使用(var previewContext=previewVisual.RenderOpen())
{
预览Context.DrawDrawing(组);
previewContext.Close();
var previewBitmap=新的RenderTargetBitmap(
预览宽度,预览宽度,
新闻部,新闻部,
像素格式(默认);
previewBitmap.Render(previewVisual);
previewFrame=BitmapFrame.Create(previewBitmap);
}
//将结果生成为JPG
使用(var content=new MemoryStream())
{
var previewEncoder=新的JpegBitmapEncoder{QualityLevel=jpegQuality};
PreviewCoder.Frames.Add(previewFrame);
预览编码器。保存(内容);
content.Flush();
返回新的ImagePreviewGeneratorTo
{
Preview=content.ToArray(),
宽度=预览宽度,
高度=高度
};
}
}
//未使用-保留仅供参考
公共IEnumerable GenerateOptimized(int pixelSize,int jpegQuality=80)
{
整数预览宽度;
国际预演;
计算尺寸(像素大小、外预览宽度、外预览宽度);
var transform=新的TransformedBitmap(
_imageFrame,新的ScaleTransform(预览宽度,预览宽度,0,0));
var previewFrame=BitmapFrame.Create(transform);
//将结果生成为JPG
使用(var result=new MemoryStream())
{
var previewEncoder=新的JpegBitmapEncoder{QualityLevel=jpegQuality};
PreviewCoder.Frames.Add(previewFrame);
预览编码器。保存(结果);
返回result.ToArray();
}
}
私有静态位图缩放模式GetScalingMode(ImagePreviewGeneratorResizeQualityEnum previewQuality)
{
开关(预览质量)
{
案例图像PreviewGeneratorResizeQualityEnum.HighQuality:
返回BitmapScalingMode.HighQuality;
案例图像PreviewGeneratorResizeQualityEnum.HighSpeed:
返回BitmapScalingMode.LowQuality;
违约:
抛出新的NotSupportedException(“指定的预览质量无效”);
}
}
专用空心计算尺寸(整数像素大小、整数宽度、整数高度)
{
var originalWidth=\u imageFrame.PixelWidth;
var originalHeight=_imageFrame.PixelHeight;
//缩放:将最长边向下减少到“X”像素,并保持纵横比
如果(原始宽度我注意到