ImageSuperposeUtils.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. using Microsoft.AspNetCore.Razor.TagHelpers;
  2. using Newtonsoft.Json;
  3. using NLog.Fluent;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Drawing;
  8. using System.Drawing.Drawing2D;
  9. using System.Drawing.Imaging;
  10. using System.Drawing.Text;
  11. using System.IO;
  12. using System.Numerics;
  13. using Wicture.DbRESTFul;
  14. using static System.Net.Mime.MediaTypeNames;
  15. using Image = System.Drawing.Image;
  16. namespace JiaZhiQuan.Common.Utils
  17. {
  18. public static class ImageSuperposeUtils
  19. {
  20. /// <summary>
  21. /// 将内容添加到底图上,返回流
  22. /// </summary>
  23. /// <param name="dto"></param>
  24. /// <returns>Stream图片流, Png格式</returns>
  25. public static Stream AddContextToImage(ImageSuperposeDTO dto)
  26. {
  27. try
  28. {
  29. // 复制原始图片到临时文件夹(复制一个防止同时修改同一个图片)
  30. File.Copy(dto.OriginImagePath, dto.TempImagePath, true);
  31. // 加载原始图片
  32. using Image originalImage = Image.FromFile(dto.TempImagePath);
  33. using Graphics graphics = Graphics.FromImage(originalImage);
  34. // 循环//处理所有叠加内容
  35. foreach (var item in dto.SuperposeDetails)
  36. {
  37. if (item.Type == SuperposeType.Text)
  38. {
  39. var text = item.Text;
  40. // 设置绘制文本的字体、大小和颜色
  41. System.Drawing.Font font = new System.Drawing.Font(text.FamilyName, text.FontSize);
  42. Brush brush = new SolidBrush(text.Color);
  43. Point position = new Point(item.X, item.Y);
  44. // 绘制文本
  45. graphics.DrawString(text.Text, font, brush, new Point(item.X, item.Y));
  46. }
  47. else if (item.Type == SuperposeType.Image)
  48. {
  49. // 读取需要叠加的图片
  50. var image = Image.FromFile(item.Image.ImagePath);
  51. if (item.Image.isFillet)
  52. {
  53. image = DrawTransparentRoundCornerImage(image);
  54. }
  55. // 设置坐标宽高
  56. int width = item.Image.Width is null ? image.Width : item.Image.Width.Value,
  57. height = item.Image.Height is null ? image.Height : item.Image.Height.Value;
  58. var rectangle = new Rectangle(item.X, item.Y, width, height);
  59. graphics.DrawImage(image, rectangle);
  60. }
  61. else if (item.Type == SuperposeType.ImageStream)
  62. {
  63. if (item.ImageStream is null)
  64. {
  65. LoggerManager.Logger.Error("生成用户海报失败,图片流为空。参数:" + JsonConvert.SerializeObject(item));
  66. }
  67. // 设置坐标宽高
  68. int width = item.ImageStream.Width.Value,
  69. height = item.ImageStream.Height.Value;
  70. // 读取需要叠加的图片
  71. var image = Image.FromStream(item.ImageStream.ImageStream);
  72. // 判断图片大小是否需要缩放
  73. if (image.Width != width || image.Height != height)
  74. {
  75. image = ScaleImage(image, width, height);
  76. }
  77. // 是否圆角
  78. if (item.ImageStream.isFillet)
  79. {
  80. image = DrawTransparentRoundCornerImage(image);
  81. }
  82. var rectangle = new Rectangle(item.X, item.Y, width, height);
  83. graphics.DrawImage(image, rectangle);
  84. image.Dispose();
  85. }
  86. }
  87. // 保存修改后的图片
  88. //originalImage.Save($@"C:\Users\gy\Pictures\海报\{dto.FileName}");
  89. MemoryStream stream = new MemoryStream();
  90. originalImage.Save(stream, ImageFormat.Png);
  91. stream.Seek(0, SeekOrigin.Begin);
  92. return stream;
  93. }
  94. finally
  95. {
  96. File.Delete(dto.TempImagePath);
  97. }
  98. }
  99. /// <summary>
  100. /// 图片更改圆角
  101. /// </summary>
  102. /// <param name="image"></param>
  103. /// <returns></returns>
  104. private static Image DrawTransparentRoundCornerImage(Image image)
  105. {
  106. Bitmap bm = new Bitmap(image.Width, image.Height);
  107. using Graphics g = Graphics.FromImage(bm);
  108. g.FillRectangle(Brushes.Transparent, new Rectangle(0, 0, image.Width, image.Height));
  109. // 将画布的区域设置为透明
  110. g.Clear(Color.Transparent);
  111. // 设置绘制质量为高质量
  112. g.SmoothingMode = SmoothingMode.HighQuality;
  113. // 创建一个圆形路径
  114. GraphicsPath path = new GraphicsPath();
  115. path.AddEllipse(0, 0, bm.Width, bm.Height);
  116. // 将剪辑区域设置为圆形路径
  117. g.SetClip(path);
  118. // 在剪辑区域内绘制原始图像
  119. g.DrawImage(image, 0, 0);
  120. // 清除剪辑区域,以便添加边框
  121. g.ResetClip();
  122. // 添加3px白色边框
  123. using (Pen borderPen = new Pen(Color.White, 3))
  124. {
  125. g.DrawEllipse(borderPen, 1, 1, bm.Width - 3, bm.Height - 3);
  126. }
  127. return bm;
  128. }
  129. /// <summary>
  130. /// 缩放图片
  131. /// </summary>
  132. /// <param name="originalImage"></param>
  133. /// <param name="newWidth"></param>
  134. /// <param name="newHeight"></param>
  135. /// <returns></returns>
  136. public static Bitmap ScaleImage(Image originalImage, int newWidth, int newHeight)
  137. {
  138. // 创建目标尺寸的位图对象
  139. Bitmap scaledImage = new Bitmap(newWidth, newHeight);
  140. // 使用 Graphics 类进行绘制
  141. using (Graphics graphics = Graphics.FromImage(scaledImage))
  142. {
  143. // 设置绘制质量,可根据需要调整
  144. graphics.CompositingQuality = CompositingQuality.HighQuality;
  145. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  146. graphics.SmoothingMode = SmoothingMode.HighQuality;
  147. // 绘制缩放后的图片
  148. graphics.DrawImage(originalImage, 0, 0, newWidth, newHeight);
  149. }
  150. return scaledImage;
  151. }
  152. }
  153. public class ImageSuperposeDTO
  154. {
  155. /// <summary>
  156. /// 文件名
  157. /// </summary>
  158. public string FileName { get; set; }
  159. /// <summary>
  160. /// 原始图片路径
  161. /// </summary>
  162. public string OriginImagePath { get; set; }
  163. /// <summary>
  164. /// 临时图片路径(需要对原始图片复制一个进行操作)
  165. /// </summary>
  166. public string TempImagePath { get; set; }
  167. /// <summary>
  168. /// 叠加内容详情
  169. /// </summary>
  170. public List<ImageSuperposeDetails> SuperposeDetails { get; set; }
  171. public void Clear()
  172. {
  173. foreach (var item in SuperposeDetails)
  174. {
  175. if (item.Type == SuperposeType.ImageStream)
  176. {
  177. item.ImageStream.ImageStream.Close();
  178. }
  179. }
  180. }
  181. }
  182. /// <summary>
  183. /// 图片叠加操作类型
  184. /// </summary>
  185. public enum SuperposeType
  186. {
  187. Text = 0,
  188. Image = 1,
  189. ImageStream = 2,
  190. ComplexText = 3
  191. }
  192. public class ImageSuperposeDetails
  193. {
  194. /// <summary>
  195. /// 叠加内容类型
  196. /// </summary>
  197. public SuperposeType Type { get; set; }
  198. /// <summary>
  199. /// 叠加的文字详情
  200. /// SuperposeType.Text时,Text不能为空
  201. /// </summary>
  202. public ImageSuperposeTextDetails Text { get; set; }
  203. /// <summary>
  204. /// 叠加的图片路径
  205. /// SuperposeType.Image时,Image不能为空
  206. /// </summary>
  207. public ImageSuperposeImageDetails Image { get; set; }
  208. /// <summary>
  209. /// 叠加的图片流
  210. /// SuperposeType.ImageStream时,ImageStream不能为空
  211. /// </summary>
  212. public ImageSuperposeImageStreamDetails ImageStream { get; set; }
  213. /// <summary>
  214. /// x坐标
  215. /// </summary>
  216. public int X { get; set; }
  217. /// <summary>
  218. /// y坐标
  219. /// </summary>
  220. public int Y { get; set; }
  221. }
  222. /// <summary>
  223. /// 叠加的文字详情
  224. /// </summary>
  225. public class ImageSuperposeTextDetails
  226. {
  227. /// <summary>
  228. /// 叠加的内容
  229. /// </summary>
  230. public string Text { get; set; }
  231. /// <summary>
  232. /// 叠加内容的颜色
  233. /// </summary>
  234. public Color Color { get; set; }
  235. /// <summary>
  236. /// 文字大小
  237. /// </summary>
  238. public int FontSize { get; set; } = 16;
  239. /// <summary>
  240. /// 字体格式
  241. /// </summary>
  242. public string FamilyName { get; set; }
  243. }
  244. /// <summary>
  245. /// 叠加的图片详情
  246. /// </summary>
  247. public class ImageSuperposeImageDetails
  248. {
  249. /// <summary>
  250. /// 叠加的图片路径
  251. /// </summary>
  252. public string ImagePath { get; set; }
  253. /// <summary>
  254. /// 宽(如果设置了,那么将覆盖图片默认的宽)
  255. /// </summary>
  256. public int? Width { get; set; }
  257. /// <summary>
  258. /// 高(如果设置了,那么将覆盖图片默认的高)
  259. /// </summary>
  260. public int? Height { get; set; }
  261. /// <summary>
  262. /// 是否需要把图片变成圆型
  263. /// </summary>
  264. public bool isFillet { get; set; }
  265. }
  266. /// <summary>
  267. /// 叠加的图片流详情
  268. /// </summary>
  269. public class ImageSuperposeImageStreamDetails
  270. {
  271. /// <summary>
  272. /// 叠加的图片路径
  273. /// </summary>
  274. public Stream ImageStream { get; set; }
  275. /// <summary>
  276. /// 宽(如果设置了,那么将覆盖图片默认的宽)
  277. /// </summary>
  278. public int? Width { get; set; }
  279. /// <summary>
  280. /// 高(如果设置了,那么将覆盖图片默认的高)
  281. /// </summary>
  282. public int? Height { get; set; }
  283. /// <summary>
  284. /// 是否需要把图片变成圆型
  285. /// </summary>
  286. public bool isFillet { get; set; }
  287. }
  288. }