StripCollageBuilder.cs 25 KB

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