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

  1. #if DRAWING
  2. using System;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.Drawing.Imaging;
  6. using System.IO;
  7. namespace Apewer
  8. {
  9. /// <summary>绘图。</summary>
  10. public static class DrawingUtility
  11. {
  12. /// <summary>优化 Graphics 对象的属性,提升绘图质量。</summary>
  13. public static Graphics Optimize(this Graphics graphics)
  14. {
  15. if (graphics != null)
  16. {
  17. graphics.CompositingMode = CompositingMode.SourceOver;
  18. graphics.CompositingQuality = CompositingQuality.HighQuality;
  19. graphics.SmoothingMode = SmoothingMode.AntiAlias;
  20. }
  21. return graphics;
  22. }
  23. /// <summary>识别图像的旋转角度并进行调整。</summary>
  24. /// <exception cref="ArgumentNullException"></exception>
  25. public static void AdjustRotation(this Image image)
  26. {
  27. if (image == null) throw new ArgumentNullException(nameof(image));
  28. // 检测旋转角度
  29. var angle = 0;
  30. if (Array.IndexOf(image.PropertyIdList, 274) > -1)
  31. {
  32. var property = image.GetPropertyItem(274);
  33. var value = property.Value[0];
  34. switch (value)
  35. {
  36. case 1: break;
  37. case 3: angle = 180; break;
  38. case 6: angle = 90; break;
  39. case 8: angle = 270; break;
  40. }
  41. }
  42. // 自适应旋转
  43. switch (angle)
  44. {
  45. case 90: image.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
  46. case 180: image.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
  47. case 270: image.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
  48. }
  49. }
  50. /// <summary>作为图像,执行回调。</summary>
  51. /// <exception cref="ArgumentNullException"></exception>
  52. /// <exception cref="ArgumentException"></exception>
  53. public static void AsImage(this byte[] imageData, Action<Image> callback)
  54. {
  55. if (imageData == null) throw new ArgumentNullException(nameof(imageData));
  56. if (callback == null) throw new ArgumentNullException(nameof(callback));
  57. using (var stream = new MemoryStream(imageData))
  58. {
  59. using (var image = Image.FromStream(stream))
  60. {
  61. AdjustRotation(image);
  62. callback.Invoke(image);
  63. }
  64. }
  65. }
  66. /// <summary>作为图像,执行回调。</summary>
  67. /// <exception cref="ArgumentNullException"></exception>
  68. /// <exception cref="ArgumentException"></exception>
  69. public static TResult AsImage<TResult>(this byte[] imageData, Func<Image, TResult> callback)
  70. {
  71. if (imageData == null) throw new ArgumentNullException(nameof(imageData));
  72. if (callback == null) throw new ArgumentNullException(nameof(callback));
  73. using (var stream = new MemoryStream(imageData))
  74. {
  75. using (var image = Image.FromStream(stream))
  76. {
  77. AdjustRotation(image);
  78. return callback.Invoke(image);
  79. }
  80. }
  81. }
  82. /// <summary>保存图像为文件。</summary>
  83. /// <exception cref="ArgumentNullException"></exception>
  84. public static byte[] Save(this Image image, ImageFormat format)
  85. {
  86. if (image == null) throw new ArgumentNullException(nameof(image));
  87. if (format == null) throw new ArgumentNullException(nameof(format));
  88. using (var memory = new MemoryStream())
  89. {
  90. image.Save(memory, format);
  91. return memory.ToArray();
  92. }
  93. }
  94. /// <summary>保存为 JPEG 文件。</summary>
  95. /// <param name="image">Image 实例。</param>
  96. /// <param name="quality">JPEG 质量,0 为最低质量,100 为最高质量。</param>
  97. /// <exception cref="ArgumentNullException" />
  98. /// <exception cref="ArgumentOutOfRangeException" />
  99. /// <exception cref="System.Runtime.InteropServices.ExternalException" />
  100. public static byte[] SaveAsJpeg(this Image image, int quality = 100)
  101. {
  102. if (image == null) throw new ArgumentNullException(nameof(image));
  103. if (quality < 0 || quality > 100) throw new ArgumentOutOfRangeException(nameof(quality));
  104. var codec = ImageCodecInfo.GetImageDecoders().Find(x => x.FormatID == ImageFormat.Jpeg.Guid);
  105. if (codec == null) throw new System.Runtime.InteropServices.ExternalException($"系统中不存在 JPEG 编码器。");
  106. var parameters = new EncoderParameters(1);
  107. parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
  108. using (var memory = new MemoryStream())
  109. {
  110. image.Save(memory, codec, parameters);
  111. return memory.ToArray();
  112. }
  113. }
  114. /// <summary>等比例缩放,生成新图像。</summary>
  115. /// <exception cref="ArgumentNullException"></exception>
  116. /// <exception cref="ArgumentOutOfRangeException"></exception>
  117. public static Image AspectScale(this Image origin, int maxSide, Color? backgroundColor = null) => AspectScale(origin, maxSide, maxSide, backgroundColor);
  118. /// <summary>等比例缩放,生成新图像。</summary>
  119. /// <exception cref="ArgumentNullException"></exception>
  120. /// <exception cref="ArgumentOutOfRangeException"></exception>
  121. public static Image AspectScale(this Image origin, int maxWidth, int maxHeight, Color? backgroundColor = null)
  122. {
  123. if (origin == null) throw new ArgumentNullException(nameof(origin));
  124. if (maxWidth < 1) throw new ArgumentOutOfRangeException(nameof(maxWidth));
  125. if (maxHeight < 1) throw new ArgumentOutOfRangeException(nameof(maxHeight));
  126. var width = origin.Width;
  127. var height = origin.Height;
  128. var radio = Convert.ToDouble(width) / Convert.ToDouble(height);
  129. if (width > maxWidth)
  130. {
  131. width = maxWidth;
  132. height = Convert.ToInt32(maxWidth / radio);
  133. }
  134. if (height > maxHeight)
  135. {
  136. width = Convert.ToInt32(maxHeight * radio);
  137. height = maxHeight;
  138. }
  139. return Scale(origin, width, height, backgroundColor);
  140. }
  141. /// <summary>缩放图像,生成新图像。</summary>
  142. /// <param name="image">原始图像。</param>
  143. /// <param name="size">新图像的大小。</param>
  144. /// <param name="backgroundColor">填充新图像背景色。</param>
  145. /// <exception cref="ArgumentNullException"></exception>
  146. /// <exception cref="ArgumentOutOfRangeException"></exception>
  147. public static Bitmap Scale(this Image image, Size size, Color? backgroundColor = null) => Scale(image, size.Width, size.Height, backgroundColor);
  148. /// <summary>缩放图像,生成新图像。</summary>
  149. /// <param name="image">原始图像。</param>
  150. /// <param name="width">新图像的宽度。</param>
  151. /// <param name="height">新图像的高度。</param>
  152. /// <param name="backgroundColor">填充新图像背景色。</param>
  153. /// <exception cref="ArgumentNullException"></exception>
  154. /// <exception cref="ArgumentOutOfRangeException"></exception>
  155. static Bitmap Scale(this Image image, int width, int height, Color? backgroundColor = null)
  156. {
  157. if (image == null) throw new ArgumentNullException(nameof(image));
  158. if (width < 1) throw new ArgumentOutOfRangeException(nameof(width));
  159. if (height < 1) throw new ArgumentOutOfRangeException(nameof(height));
  160. // 检查原始图像是否为透明格式
  161. var rawFormat = image.RawFormat;
  162. var isTransparentFormat = rawFormat.Equals(ImageFormat.Png) || rawFormat.Equals(ImageFormat.Gif);
  163. var pixelFormat = isTransparentFormat ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
  164. var bitmap = new Bitmap(width, height, pixelFormat);
  165. try
  166. {
  167. using (var graphic = Graphics.FromImage(bitmap))
  168. {
  169. graphic.CompositingMode = CompositingMode.SourceOver;
  170. graphic.CompositingQuality = CompositingQuality.HighQuality;
  171. graphic.SmoothingMode = SmoothingMode.AntiAlias;
  172. // 填充背景色
  173. if (isTransparentFormat)
  174. {
  175. if (backgroundColor != null && backgroundColor.HasValue) graphic.Clear(backgroundColor.Value);
  176. else graphic.Clear(Color.Transparent);
  177. }
  178. // 绘制新图像
  179. graphic.DrawImage(image, 0, 0, width, height);
  180. }
  181. return bitmap;
  182. }
  183. catch
  184. {
  185. bitmap.Dispose();
  186. throw;
  187. }
  188. }
  189. }
  190. }
  191. #endif