ImageSuperposeUtils.cs 11 KB

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