StripCollageBuilder.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. using ImageMagickSharp;
  2. using MediaBrowser.Common.Configuration;
  3. using System;
  4. using System.Collections.Generic;
  5. using CommonIO;
  6. using MediaBrowser.Common.IO;
  7. namespace Emby.Drawing.ImageMagick
  8. {
  9. public class StripCollageBuilder
  10. {
  11. private readonly IApplicationPaths _appPaths;
  12. private readonly IFileSystem _fileSystem;
  13. public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
  14. {
  15. _appPaths = appPaths;
  16. _fileSystem = fileSystem;
  17. }
  18. public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height, string text)
  19. {
  20. if (!string.IsNullOrWhiteSpace(text))
  21. {
  22. using (var wand = BuildPosterCollageWandWithText(paths, text, width, height))
  23. {
  24. wand.SaveImage(outputPath);
  25. }
  26. }
  27. else
  28. {
  29. using (var wand = BuildPosterCollageWand(paths, width, height))
  30. {
  31. wand.SaveImage(outputPath);
  32. }
  33. }
  34. }
  35. public void BuildSquareCollage(List<string> paths, string outputPath, int width, int height, string text)
  36. {
  37. if (!string.IsNullOrWhiteSpace(text))
  38. {
  39. using (var wand = BuildSquareCollageWandWithText(paths, text, width, height))
  40. {
  41. wand.SaveImage(outputPath);
  42. }
  43. }
  44. else
  45. {
  46. using (var wand = BuildSquareCollageWand(paths, width, height))
  47. {
  48. wand.SaveImage(outputPath);
  49. }
  50. }
  51. }
  52. public void BuildThumbCollage(List<string> paths, string outputPath, int width, int height, string text)
  53. {
  54. if (!string.IsNullOrWhiteSpace(text))
  55. {
  56. using (var wand = BuildThumbCollageWandWithText(paths, text, width, height))
  57. {
  58. wand.SaveImage(outputPath);
  59. }
  60. }
  61. else
  62. {
  63. using (var wand = BuildThumbCollageWand(paths, width, height))
  64. {
  65. wand.SaveImage(outputPath);
  66. }
  67. }
  68. }
  69. private MagickWand BuildThumbCollageWandWithText(List<string> paths, string text, int width, int height)
  70. {
  71. var inputPaths = ImageHelpers.ProjectPaths(paths, 8);
  72. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  73. {
  74. var wand = new MagickWand(width, height);
  75. wand.OpenImage("gradient:#111111-#111111");
  76. using (var draw = new DrawingWand())
  77. {
  78. using (var fcolor = new PixelWand(ColorName.White))
  79. {
  80. draw.FillColor = fcolor;
  81. draw.Font = MontserratLightFont;
  82. draw.FontSize = 60;
  83. draw.FontWeight = FontWeightType.LightStyle;
  84. draw.TextAntialias = true;
  85. }
  86. var fontMetrics = wand.QueryFontMetrics(draw, text);
  87. var textContainerY = Convert.ToInt32(height * .165);
  88. wand.CurrentImage.AnnotateImage(draw, (width - fontMetrics.TextWidth) / 2, textContainerY, 0.0, text);
  89. var iSlice = Convert.ToInt32(width * .1166666667);
  90. int iTrans = Convert.ToInt32(height * 0.2);
  91. int iHeight = Convert.ToInt32(height * 0.46296296296296296296296296296296);
  92. var horizontalImagePadding = Convert.ToInt32(width * 0.0125);
  93. foreach (var element in wandImages.ImageList)
  94. {
  95. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  96. element.Gravity = GravityType.CenterGravity;
  97. element.BackgroundColor = new PixelWand("none", 1);
  98. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  99. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  100. element.CropImage(iSlice, iHeight, ix, 0);
  101. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  102. }
  103. wandImages.SetFirstIterator();
  104. using (var wandList = wandImages.AppendImages())
  105. {
  106. wandList.CurrentImage.TrimImage(1);
  107. using (var mwr = wandList.CloneMagickWand())
  108. {
  109. using (var blackPixelWand = new PixelWand(ColorName.Black))
  110. {
  111. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  112. {
  113. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  114. mwr.CurrentImage.FlipImage();
  115. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  116. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  117. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  118. {
  119. mwg.OpenImage("gradient:black-none");
  120. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  121. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.DstInCompositeOp, 0, verticalSpacing);
  122. wandList.AddImage(mwr);
  123. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  124. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * 0.26851851851851851851851851851852));
  125. }
  126. }
  127. }
  128. }
  129. }
  130. }
  131. return wand;
  132. }
  133. }
  134. private MagickWand BuildPosterCollageWand(List<string> paths, int width, int height)
  135. {
  136. var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
  137. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  138. {
  139. var wand = new MagickWand(width, height);
  140. wand.OpenImage("gradient:#111111-#111111");
  141. using (var draw = new DrawingWand())
  142. {
  143. var iSlice = Convert.ToInt32(width * 0.3);
  144. int iTrans = Convert.ToInt32(height * .25);
  145. int iHeight = Convert.ToInt32(height * .65);
  146. var horizontalImagePadding = Convert.ToInt32(width * 0.0366);
  147. foreach (var element in wandImages.ImageList)
  148. {
  149. using (var blackPixelWand = new PixelWand(ColorName.Black))
  150. {
  151. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  152. element.Gravity = GravityType.CenterGravity;
  153. element.BackgroundColor = blackPixelWand;
  154. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  155. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  156. element.CropImage(iSlice, iHeight, ix, 0);
  157. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  158. }
  159. }
  160. wandImages.SetFirstIterator();
  161. using (var wandList = wandImages.AppendImages())
  162. {
  163. wandList.CurrentImage.TrimImage(1);
  164. using (var mwr = wandList.CloneMagickWand())
  165. {
  166. using (var blackPixelWand = new PixelWand(ColorName.Black))
  167. {
  168. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  169. {
  170. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  171. mwr.CurrentImage.FlipImage();
  172. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  173. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  174. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  175. {
  176. mwg.OpenImage("gradient:black-none");
  177. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  178. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.CopyOpacityCompositeOp, 0, verticalSpacing);
  179. wandList.AddImage(mwr);
  180. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  181. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * .05));
  182. }
  183. }
  184. }
  185. }
  186. }
  187. }
  188. return wand;
  189. }
  190. }
  191. private MagickWand BuildPosterCollageWandWithText(List<string> paths, string label, int width, int height)
  192. {
  193. var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
  194. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  195. {
  196. var wand = new MagickWand(width, height);
  197. wand.OpenImage("gradient:#111111-#111111");
  198. using (var draw = new DrawingWand())
  199. {
  200. using (var fcolor = new PixelWand(ColorName.White))
  201. {
  202. draw.FillColor = fcolor;
  203. draw.Font = MontserratLightFont;
  204. draw.FontSize = 60;
  205. draw.FontWeight = FontWeightType.LightStyle;
  206. draw.TextAntialias = true;
  207. }
  208. var fontMetrics = wand.QueryFontMetrics(draw, label);
  209. var textContainerY = Convert.ToInt32(height * .165);
  210. wand.CurrentImage.AnnotateImage(draw, (width - fontMetrics.TextWidth) / 2, textContainerY, 0.0, label);
  211. var iSlice = Convert.ToInt32(width * 0.225);
  212. int iTrans = Convert.ToInt32(height * 0.2);
  213. int iHeight = Convert.ToInt32(height * 0.46296296296296296296296296296296);
  214. var horizontalImagePadding = Convert.ToInt32(width * 0.0275);
  215. foreach (var element in wandImages.ImageList)
  216. {
  217. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  218. element.Gravity = GravityType.CenterGravity;
  219. element.BackgroundColor = new PixelWand("none", 1);
  220. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  221. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  222. element.CropImage(iSlice, iHeight, ix, 0);
  223. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  224. }
  225. wandImages.SetFirstIterator();
  226. using (var wandList = wandImages.AppendImages())
  227. {
  228. wandList.CurrentImage.TrimImage(1);
  229. using (var mwr = wandList.CloneMagickWand())
  230. {
  231. using (var blackPixelWand = new PixelWand(ColorName.Black))
  232. {
  233. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  234. {
  235. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  236. mwr.CurrentImage.FlipImage();
  237. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  238. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  239. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  240. {
  241. mwg.OpenImage("gradient:black-none");
  242. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  243. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.DstInCompositeOp, 0, verticalSpacing);
  244. wandList.AddImage(mwr);
  245. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  246. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * 0.26851851851851851851851851851852));
  247. }
  248. }
  249. }
  250. }
  251. }
  252. }
  253. return wand;
  254. }
  255. }
  256. private MagickWand BuildThumbCollageWand(List<string> paths, int width, int height)
  257. {
  258. var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
  259. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  260. {
  261. var wand = new MagickWand(width, height);
  262. wand.OpenImage("gradient:#111111-#111111");
  263. using (var draw = new DrawingWand())
  264. {
  265. var iSlice = Convert.ToInt32(width * .1166666667 * 2);
  266. int iTrans = Convert.ToInt32(height * .25);
  267. int iHeight = Convert.ToInt32(height * .62);
  268. var horizontalImagePadding = Convert.ToInt32(width * 0.0125);
  269. foreach (var element in wandImages.ImageList)
  270. {
  271. using (var blackPixelWand = new PixelWand(ColorName.Black))
  272. {
  273. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  274. element.Gravity = GravityType.CenterGravity;
  275. element.BackgroundColor = blackPixelWand;
  276. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  277. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  278. element.CropImage(iSlice, iHeight, ix, 0);
  279. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  280. }
  281. }
  282. wandImages.SetFirstIterator();
  283. using (var wandList = wandImages.AppendImages())
  284. {
  285. wandList.CurrentImage.TrimImage(1);
  286. using (var mwr = wandList.CloneMagickWand())
  287. {
  288. using (var blackPixelWand = new PixelWand(ColorName.Black))
  289. {
  290. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  291. {
  292. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  293. mwr.CurrentImage.FlipImage();
  294. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  295. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  296. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  297. {
  298. mwg.OpenImage("gradient:black-none");
  299. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  300. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.CopyOpacityCompositeOp, 0, verticalSpacing);
  301. wandList.AddImage(mwr);
  302. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  303. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * .085));
  304. }
  305. }
  306. }
  307. }
  308. }
  309. }
  310. return wand;
  311. }
  312. }
  313. private MagickWand BuildSquareCollageWand(List<string> paths, int width, int height)
  314. {
  315. var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
  316. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  317. {
  318. var wand = new MagickWand(width, height);
  319. wand.OpenImage("gradient:#111111-#111111");
  320. using (var draw = new DrawingWand())
  321. {
  322. var iSlice = Convert.ToInt32(width * .3);
  323. int iTrans = Convert.ToInt32(height * .25);
  324. int iHeight = Convert.ToInt32(height * .63);
  325. var horizontalImagePadding = Convert.ToInt32(width * 0.02);
  326. foreach (var element in wandImages.ImageList)
  327. {
  328. using (var blackPixelWand = new PixelWand(ColorName.Black))
  329. {
  330. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  331. element.Gravity = GravityType.CenterGravity;
  332. element.BackgroundColor = blackPixelWand;
  333. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  334. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  335. element.CropImage(iSlice, iHeight, ix, 0);
  336. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  337. }
  338. }
  339. wandImages.SetFirstIterator();
  340. using (var wandList = wandImages.AppendImages())
  341. {
  342. wandList.CurrentImage.TrimImage(1);
  343. using (var mwr = wandList.CloneMagickWand())
  344. {
  345. using (var blackPixelWand = new PixelWand(ColorName.Black))
  346. {
  347. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  348. {
  349. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  350. mwr.CurrentImage.FlipImage();
  351. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  352. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  353. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  354. {
  355. mwg.OpenImage("gradient:black-none");
  356. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  357. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.CopyOpacityCompositeOp, 0, verticalSpacing);
  358. wandList.AddImage(mwr);
  359. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  360. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * .07));
  361. }
  362. }
  363. }
  364. }
  365. }
  366. }
  367. return wand;
  368. }
  369. }
  370. private MagickWand BuildSquareCollageWandWithText(List<string> paths, string label, int width, int height)
  371. {
  372. var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
  373. using (var wandImages = new MagickWand(inputPaths.ToArray()))
  374. {
  375. var wand = new MagickWand(width, height);
  376. wand.OpenImage("gradient:#111111-#111111");
  377. using (var draw = new DrawingWand())
  378. {
  379. using (var fcolor = new PixelWand(ColorName.White))
  380. {
  381. draw.FillColor = fcolor;
  382. draw.Font = MontserratLightFont;
  383. draw.FontSize = 60;
  384. draw.FontWeight = FontWeightType.LightStyle;
  385. draw.TextAntialias = true;
  386. }
  387. var fontMetrics = wand.QueryFontMetrics(draw, label);
  388. var textContainerY = Convert.ToInt32(height * .165);
  389. wand.CurrentImage.AnnotateImage(draw, (width - fontMetrics.TextWidth) / 2, textContainerY, 0.0, label);
  390. var iSlice = Convert.ToInt32(width * .225);
  391. int iTrans = Convert.ToInt32(height * 0.2);
  392. int iHeight = Convert.ToInt32(height * 0.46296296296296296296296296296296);
  393. var horizontalImagePadding = Convert.ToInt32(width * 0.02);
  394. foreach (var element in wandImages.ImageList)
  395. {
  396. int iWidth = (int)Math.Abs(iHeight * element.Width / element.Height);
  397. element.Gravity = GravityType.CenterGravity;
  398. element.BackgroundColor = new PixelWand("none", 1);
  399. element.ResizeImage(iWidth, iHeight, FilterTypes.LanczosFilter);
  400. int ix = (int)Math.Abs((iWidth - iSlice) / 2);
  401. element.CropImage(iSlice, iHeight, ix, 0);
  402. element.ExtentImage(iSlice, iHeight, 0 - horizontalImagePadding, 0);
  403. }
  404. wandImages.SetFirstIterator();
  405. using (var wandList = wandImages.AppendImages())
  406. {
  407. wandList.CurrentImage.TrimImage(1);
  408. using (var mwr = wandList.CloneMagickWand())
  409. {
  410. using (var blackPixelWand = new PixelWand(ColorName.Black))
  411. {
  412. using (var greyPixelWand = new PixelWand(ColorName.Grey70))
  413. {
  414. mwr.CurrentImage.ResizeImage(wandList.CurrentImage.Width, (wandList.CurrentImage.Height / 2), FilterTypes.LanczosFilter, 1);
  415. mwr.CurrentImage.FlipImage();
  416. mwr.CurrentImage.AlphaChannel = AlphaChannelType.DeactivateAlphaChannel;
  417. mwr.CurrentImage.ColorizeImage(blackPixelWand, greyPixelWand);
  418. using (var mwg = new MagickWand(wandList.CurrentImage.Width, iTrans))
  419. {
  420. mwg.OpenImage("gradient:black-none");
  421. var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
  422. mwr.CurrentImage.CompositeImage(mwg, CompositeOperator.DstInCompositeOp, 0, verticalSpacing);
  423. wandList.AddImage(mwr);
  424. int ex = (int)(wand.CurrentImage.Width - mwg.CurrentImage.Width) / 2;
  425. wand.CurrentImage.CompositeImage(wandList.AppendImages(true), CompositeOperator.AtopCompositeOp, ex, Convert.ToInt32(height * 0.26851851851851851851851851851852));
  426. }
  427. }
  428. }
  429. }
  430. }
  431. }
  432. return wand;
  433. }
  434. }
  435. private string MontserratLightFont
  436. {
  437. get { return PlayedIndicatorDrawer.ExtractFont("MontserratLight.otf", _appPaths, _fileSystem); }
  438. }
  439. }
  440. }