Preloader.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. using PicView.ImageHandling;
  2. using System.Collections.Concurrent;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Threading.Tasks;
  6. using System.Windows.Media.Imaging;
  7. using static PicView.Library.Fields;
  8. namespace PicView.ChangeImage
  9. {
  10. /// <summary>
  11. /// Used for containing a list of BitmapSources
  12. /// </summary>
  13. internal static class Preloader
  14. {
  15. /// <summary>
  16. /// Start preload every third entry
  17. /// </summary>
  18. /// <returns></returns>
  19. internal static bool StartPreload()
  20. {
  21. if (FreshStartup || PreloadCount > 2 || PreloadCount < -2)
  22. {
  23. PreloadCount = 0;
  24. return true;
  25. }
  26. return false;
  27. }
  28. /// <summary>
  29. /// Preloader list of BitmapSources
  30. /// </summary>
  31. private static readonly ConcurrentDictionary<string, BitmapSource> Sources = new ConcurrentDictionary<string, BitmapSource>();
  32. /// <summary>
  33. /// When Preloader is adding an image
  34. /// </summary>
  35. internal static bool IsLoading;
  36. /// <summary>
  37. /// Used for manual loading fix
  38. /// </summary>
  39. internal static bool IsReset;
  40. //internal static int Count { get { return Sources.Count; } }
  41. /// <summary>
  42. /// Add file to prelader
  43. /// </summary>
  44. /// <param name="file">file path</param>
  45. internal static async Task<bool> Add(string file)
  46. {
  47. if (Contains(file) || Pics.Count == 0 || Pics.IndexOf(file) == -1)
  48. {
  49. return IsLoading = false;
  50. }
  51. IsLoading = true;
  52. var pic = await ImageDecoder.RenderToBitmapSource(file).ConfigureAwait(true);
  53. if (pic == null)
  54. {
  55. return IsLoading = false;
  56. }
  57. if (!pic.IsFrozen)
  58. {
  59. pic.Freeze();
  60. }
  61. IsLoading = false;
  62. #if DEBUG
  63. Trace.WriteLine("Add string file = " + file + " to Preloader, index " + Pics.IndexOf(file));
  64. #endif
  65. return Sources.TryAdd(file, pic);
  66. }
  67. /// <summary>
  68. /// Add file to preloader from index
  69. /// </summary>
  70. /// <param name="i">Index of Pics</param>
  71. internal static async Task<bool> Add(int i)
  72. {
  73. if (i >= Pics.Count || i < 0)
  74. {
  75. return false;
  76. }
  77. IsLoading = true;
  78. if (File.Exists(Pics[i]))
  79. {
  80. if (!Contains(Pics[i]))
  81. {
  82. return await Add(Pics[i]).ConfigureAwait(true);
  83. }
  84. else
  85. {
  86. #if DEBUG
  87. Trace.WriteLine("Skipped at index " + i);
  88. #endif
  89. return false;
  90. }
  91. }
  92. else
  93. {
  94. Pics.Remove(Pics[i]);
  95. #if DEBUG
  96. Trace.WriteLine("Preloader removed = " + Pics[i] + " from Pics, index " + i);
  97. #endif
  98. return IsLoading = false;
  99. }
  100. }
  101. internal static void Add(BitmapSource bmp, string key)
  102. {
  103. if (string.IsNullOrWhiteSpace(key))
  104. {
  105. #if DEBUG
  106. Trace.WriteLine("Preloader.Add key is null");
  107. #endif
  108. return;
  109. }
  110. if (Contains(key))
  111. {
  112. #if DEBUG
  113. Trace.WriteLine("Preloader.Add already contains " + key);
  114. #endif
  115. return;
  116. }
  117. if (bmp == null)
  118. {
  119. #if DEBUG
  120. Trace.WriteLine("Preloader.Add bmp null " + key);
  121. #endif
  122. return;
  123. }
  124. if (!bmp.IsFrozen)
  125. {
  126. bmp.Freeze();
  127. }
  128. #if DEBUG
  129. if (Sources.TryAdd(key, bmp))
  130. {
  131. Trace.WriteLine("Manually added = " + key + " to Preloader, index " + Pics.IndexOf(key));
  132. }
  133. else
  134. {
  135. Trace.WriteLine("Preloader failed to add = " + key + " , index " + Pics.IndexOf(key));
  136. }
  137. #else
  138. Sources.TryAdd(key, bmp);
  139. #endif
  140. }
  141. /// <summary>
  142. /// Removes the key, after checking if it exists
  143. /// </summary>
  144. /// <param name="key"></param>
  145. internal static void Remove(string key)
  146. {
  147. if (key == null)
  148. {
  149. #if DEBUG
  150. Trace.WriteLine("Preloader.Remove key null, " + key);
  151. #endif
  152. return;
  153. }
  154. if (!Contains(key))
  155. {
  156. #if DEBUG
  157. Trace.WriteLine("Preloader.Remove does not contain " + key);
  158. #endif
  159. return;
  160. }
  161. _ = Sources[key];
  162. #if DEBUG
  163. if (Sources.TryRemove(key, out _))
  164. {
  165. Trace.WriteLine("Removed = " + key + " from Preloader, index " + Pics.IndexOf(key));
  166. }
  167. else
  168. {
  169. Trace.WriteLine("Failed to Remove = " + key + " from Preloader, index " + Pics.IndexOf(key));
  170. }
  171. #else
  172. Sources.TryRemove(key, out _);
  173. #endif
  174. }
  175. /// <summary>
  176. /// Removes the key, after checking if it exists
  177. /// </summary>
  178. /// <param name="key"></param>
  179. internal static void Remove(int i)
  180. {
  181. if (i >= Pics.Count || i < 0)
  182. {
  183. return;
  184. }
  185. if (File.Exists(Pics[i]))
  186. {
  187. if (Contains(Pics[i]))
  188. {
  189. Remove(Pics[i]);
  190. }
  191. }
  192. }
  193. /// <summary>
  194. /// Removes all keys
  195. /// </summary>
  196. internal static void Clear()
  197. {
  198. if (Sources.Count <= 0)
  199. {
  200. return;
  201. }
  202. Sources.Clear();
  203. PreloadCount = 4; // Reset to make sure
  204. #if DEBUG
  205. Trace.WriteLine("Cleared Preloader");
  206. #endif
  207. }
  208. /// <summary>
  209. /// Removes specific keys
  210. /// </summary>
  211. /// <param name="array"></param>
  212. internal static void Clear(string[] array)
  213. {
  214. for (int i = 0; i < array.Length; i++)
  215. {
  216. Remove(array[i]);
  217. #if DEBUG
  218. Trace.WriteLine("Removed = " + array[i] + " from Preloader");
  219. #endif
  220. }
  221. }
  222. /// <summary>
  223. /// Returns the specified BitmapSource.
  224. /// Returns null if key not found.
  225. /// </summary>
  226. /// <param name="key"></param>
  227. /// <returns></returns>
  228. internal static BitmapSource Load(string key)
  229. {
  230. if (string.IsNullOrWhiteSpace(key) || !Contains(key))
  231. {
  232. return null;
  233. }
  234. return Sources[key];
  235. }
  236. /// <summary>
  237. /// Checks if the specified key exists
  238. /// </summary>
  239. /// <param name="key"></param>
  240. /// <returns></returns>
  241. internal static bool Contains(string key)
  242. {
  243. if (string.IsNullOrWhiteSpace(key) || Sources.Count <= 0)
  244. {
  245. return false;
  246. }
  247. return Sources.ContainsKey(key);
  248. }
  249. internal static async void PreloaderFix(string file)
  250. {
  251. if (!Contains(file))
  252. {
  253. PreloadCount = 4;
  254. IsReset = true;
  255. await Add(file).ConfigureAwait(false);
  256. }
  257. }
  258. /// <summary>
  259. /// Starts decoding images into memory,
  260. /// based on current index and if reverse or not
  261. /// </summary>
  262. /// <param name="index"></param>
  263. /// <param name="reverse"></param>
  264. internal static Task PreLoad(int index)
  265. {
  266. #if DEBUG
  267. Trace.WriteLine("Preolader started, "
  268. + string.Concat(Properties.Settings.Default.Looping ? "looping " : string.Empty)
  269. + string.Concat(Reverse ? "backwards" : "forwards"));
  270. #endif
  271. return Task.Run(async () =>
  272. {
  273. var toLoad = 8;
  274. var extraToLoad = toLoad / 2;
  275. var cleanUp = toLoad + extraToLoad;
  276. // Not looping
  277. if (!Properties.Settings.Default.Looping)
  278. {
  279. // Forwards
  280. if (!Reverse)
  281. {
  282. // Add first elements
  283. for (int i = index + 1; i < (index + 1) + toLoad; i++)
  284. {
  285. if (i > Pics.Count)
  286. {
  287. break;
  288. }
  289. await Add(i).ConfigureAwait(false);
  290. }
  291. // Add second elements behind
  292. for (int i = index - 1; i > (index - 1) - extraToLoad; i--)
  293. {
  294. if (i < 0)
  295. {
  296. break;
  297. }
  298. await Add(i).ConfigureAwait(false);
  299. }
  300. //Clean up behind
  301. if (Pics.Count > cleanUp * 2 && !FreshStartup)
  302. {
  303. for (int i = (index - 1) - cleanUp; i < ((index - 1) - extraToLoad); i++)
  304. {
  305. if (i < 0)
  306. {
  307. continue;
  308. }
  309. if (i > Pics.Count)
  310. {
  311. break;
  312. }
  313. Remove(i);
  314. }
  315. }
  316. }
  317. // Backwards
  318. else
  319. {
  320. // Add first elements behind
  321. for (int i = index - 1; i > (index - 1) - toLoad; i--)
  322. {
  323. if (i < 0)
  324. {
  325. break;
  326. }
  327. await Add(i).ConfigureAwait(false);
  328. }
  329. // Add second elements
  330. for (int i = index + 1; i <= (index + 1) + toLoad; i++)
  331. {
  332. if (i > Pics.Count)
  333. {
  334. break;
  335. }
  336. await Add(i).ConfigureAwait(false);
  337. }
  338. //Clean up infront
  339. if (Pics.Count > cleanUp * 2 && !FreshStartup)
  340. {
  341. for (int i = (index + 1) + cleanUp; i > ((index + 1) + cleanUp) - extraToLoad; i--)
  342. {
  343. if (i < 0)
  344. {
  345. continue;
  346. }
  347. if (i > Pics.Count)
  348. {
  349. break;
  350. }
  351. Remove(i);
  352. }
  353. }
  354. }
  355. }
  356. // Looping!
  357. else
  358. {
  359. // Forwards
  360. if (!Reverse)
  361. {
  362. // Add first elements
  363. for (int i = index + 1; i < (index + 1) + toLoad; i++)
  364. {
  365. await Add(i % Pics.Count).ConfigureAwait(false);
  366. }
  367. // Add second elements behind
  368. for (int i = index - 1; i > (index - 1) - extraToLoad; i--)
  369. {
  370. await Add(i % Pics.Count).ConfigureAwait(false);
  371. }
  372. //Clean up behind
  373. if (Pics.Count > cleanUp * 2 && !FreshStartup)
  374. {
  375. for (int i = (index - 1) - cleanUp; i < ((index - 1) - extraToLoad); i++)
  376. {
  377. Remove(i % Pics.Count);
  378. }
  379. }
  380. }
  381. // Backwards
  382. else
  383. {
  384. // Add first elements behind
  385. int y = 0;
  386. for (int i = index - 1; i > (index - 1) - toLoad; i--)
  387. {
  388. y++;
  389. if (i < 0)
  390. {
  391. for (int x = Pics.Count - 1; x >= Pics.Count - y; x--)
  392. {
  393. await Add(x).ConfigureAwait(false);
  394. }
  395. break;
  396. }
  397. await Add(i).ConfigureAwait(false);
  398. }
  399. // Add second elements
  400. for (int i = index + 1; i <= (index + 1) + toLoad; i++)
  401. {
  402. await Add(i % Pics.Count).ConfigureAwait(false);
  403. }
  404. //Clean up infront
  405. if (Pics.Count > cleanUp + toLoad && !FreshStartup)
  406. {
  407. for (int i = (index + 1) + cleanUp; i > ((index + 1) + cleanUp) - extraToLoad; i--)
  408. {
  409. Remove(i % Pics.Count);
  410. }
  411. }
  412. }
  413. }
  414. IsLoading = false; // Fixes loading erros
  415. });
  416. }
  417. }
  418. }