You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
9.1 KiB
222 lines
9.1 KiB
#if DRAWING
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
|
|
namespace Apewer
|
|
{
|
|
|
|
/// <summary>绘图。</summary>
|
|
public static class DrawingUtility
|
|
{
|
|
|
|
/// <summary>优化 Graphics 对象的属性,提升绘图质量。</summary>
|
|
public static Graphics Optimize(this Graphics graphics)
|
|
{
|
|
if (graphics != null)
|
|
{
|
|
graphics.CompositingMode = CompositingMode.SourceOver;
|
|
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
|
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
}
|
|
return graphics;
|
|
}
|
|
|
|
/// <summary>识别图像的旋转角度并进行调整。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static void AdjustRotation(this Image image)
|
|
{
|
|
if (image == null) throw new ArgumentNullException(nameof(image));
|
|
|
|
// 检测旋转角度
|
|
var angle = 0;
|
|
if (Array.IndexOf(image.PropertyIdList, 274) > -1)
|
|
{
|
|
var property = image.GetPropertyItem(274);
|
|
var value = property.Value[0];
|
|
switch (value)
|
|
{
|
|
case 1: break;
|
|
case 3: angle = 180; break;
|
|
case 6: angle = 90; break;
|
|
case 8: angle = 270; break;
|
|
}
|
|
}
|
|
|
|
// 自适应旋转
|
|
switch (angle)
|
|
{
|
|
case 90: image.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
|
|
case 180: image.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
|
|
case 270: image.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
|
|
}
|
|
}
|
|
|
|
/// <summary>作为图像,执行回调。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
public static void AsImage(this byte[] imageData, Action<Image> callback)
|
|
{
|
|
if (imageData == null) throw new ArgumentNullException(nameof(imageData));
|
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
|
|
|
using (var stream = new MemoryStream(imageData))
|
|
{
|
|
using (var image = Image.FromStream(stream))
|
|
{
|
|
AdjustRotation(image);
|
|
callback.Invoke(image);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>作为图像,执行回调。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
public static TResult AsImage<TResult>(this byte[] imageData, Func<Image, TResult> callback)
|
|
{
|
|
if (imageData == null) throw new ArgumentNullException(nameof(imageData));
|
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
|
|
|
using (var stream = new MemoryStream(imageData))
|
|
{
|
|
using (var image = Image.FromStream(stream))
|
|
{
|
|
AdjustRotation(image);
|
|
return callback.Invoke(image);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>保存图像为文件。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static byte[] Save(this Image image, ImageFormat format)
|
|
{
|
|
if (image == null) throw new ArgumentNullException(nameof(image));
|
|
if (format == null) throw new ArgumentNullException(nameof(format));
|
|
|
|
using (var memory = new MemoryStream())
|
|
{
|
|
image.Save(memory, format);
|
|
return memory.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>保存为 JPEG 文件。</summary>
|
|
/// <param name="image">Image 实例。</param>
|
|
/// <param name="quality">JPEG 质量,0 为最低质量,100 为最高质量。</param>
|
|
/// <exception cref="ArgumentNullException" />
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
/// <exception cref="System.Runtime.InteropServices.ExternalException" />
|
|
public static byte[] SaveAsJpeg(this Image image, int quality = 100)
|
|
{
|
|
if (image == null) throw new ArgumentNullException(nameof(image));
|
|
if (quality < 0 || quality > 100) throw new ArgumentOutOfRangeException(nameof(quality));
|
|
|
|
var codec = ImageCodecInfo.GetImageDecoders().Find(x => x.FormatID == ImageFormat.Jpeg.Guid);
|
|
if (codec == null) throw new System.Runtime.InteropServices.ExternalException($"系统中不存在 JPEG 编码器。");
|
|
|
|
var parameters = new EncoderParameters(1);
|
|
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
|
|
|
|
using (var memory = new MemoryStream())
|
|
{
|
|
image.Save(memory, codec, parameters);
|
|
return memory.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>等比例缩放,生成新图像。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
public static Image AspectScale(this Image origin, int maxSide, Color? backgroundColor = null) => AspectScale(origin, maxSide, maxSide, backgroundColor);
|
|
|
|
/// <summary>等比例缩放,生成新图像。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
public static Image AspectScale(this Image origin, int maxWidth, int maxHeight, Color? backgroundColor = null)
|
|
{
|
|
if (origin == null) throw new ArgumentNullException(nameof(origin));
|
|
if (maxWidth < 1) throw new ArgumentOutOfRangeException(nameof(maxWidth));
|
|
if (maxHeight < 1) throw new ArgumentOutOfRangeException(nameof(maxHeight));
|
|
|
|
var width = origin.Width;
|
|
var height = origin.Height;
|
|
|
|
var radio = Convert.ToDouble(width) / Convert.ToDouble(height);
|
|
if (width > maxWidth)
|
|
{
|
|
width = maxWidth;
|
|
height = Convert.ToInt32(maxWidth / radio);
|
|
}
|
|
if (height > maxHeight)
|
|
{
|
|
width = Convert.ToInt32(maxHeight * radio);
|
|
height = maxHeight;
|
|
}
|
|
|
|
return Scale(origin, width, height, backgroundColor);
|
|
}
|
|
|
|
/// <summary>缩放图像,生成新图像。</summary>
|
|
/// <param name="image">原始图像。</param>
|
|
/// <param name="size">新图像的大小。</param>
|
|
/// <param name="backgroundColor">填充新图像背景色。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
public static Bitmap Scale(this Image image, Size size, Color? backgroundColor = null) => Scale(image, size.Width, size.Height, backgroundColor);
|
|
|
|
/// <summary>缩放图像,生成新图像。</summary>
|
|
/// <param name="image">原始图像。</param>
|
|
/// <param name="width">新图像的宽度。</param>
|
|
/// <param name="height">新图像的高度。</param>
|
|
/// <param name="backgroundColor">填充新图像背景色。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
static Bitmap Scale(this Image image, int width, int height, Color? backgroundColor = null)
|
|
{
|
|
if (image == null) throw new ArgumentNullException(nameof(image));
|
|
if (width < 1) throw new ArgumentOutOfRangeException(nameof(width));
|
|
if (height < 1) throw new ArgumentOutOfRangeException(nameof(height));
|
|
|
|
// 检查原始图像是否为透明格式
|
|
var rawFormat = image.RawFormat;
|
|
var isTransparentFormat = rawFormat.Equals(ImageFormat.Png) || rawFormat.Equals(ImageFormat.Gif);
|
|
var pixelFormat = isTransparentFormat ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
|
|
|
|
var bitmap = new Bitmap(width, height, pixelFormat);
|
|
try
|
|
{
|
|
using (var graphic = Graphics.FromImage(bitmap))
|
|
{
|
|
graphic.CompositingMode = CompositingMode.SourceOver;
|
|
graphic.CompositingQuality = CompositingQuality.HighQuality;
|
|
graphic.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
// 填充背景色
|
|
if (isTransparentFormat)
|
|
{
|
|
if (backgroundColor != null && backgroundColor.HasValue) graphic.Clear(backgroundColor.Value);
|
|
else graphic.Clear(Color.Transparent);
|
|
}
|
|
|
|
// 绘制新图像
|
|
graphic.DrawImage(image, 0, 0, width, height);
|
|
}
|
|
return bitmap;
|
|
}
|
|
catch
|
|
{
|
|
bitmap.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|