using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Dominion.NET_WPF.Caching { public class ImageRepository { #if (DEBUG) public static readonly string ImageRoot = Directory.GetParent(Directory.GetParent(Directory.GetParent(Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).FullName).FullName).FullName).FullName; #else public static readonly String ImageRoot = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).FullName; #endif private static readonly Dictionary> BitmapImageCache = new Dictionary>(); private static readonly Dictionary ForegroundBrushCache = new Dictionary(); private static readonly System.Threading.Mutex Mutex; private static readonly ImageRepository Instance; private static readonly string[] IGNORED_PATTERNS = new string[] { "Thumbs.db" }; private ImageRepository() { } static ImageRepository() { Instance = new ImageRepository(); Mutex = new System.Threading.Mutex(); } public static void Reset() { BitmapImageCache.Clear(); } public static ImageRepository Acquire() { Mutex.WaitOne(); return Instance; } public static void Release() { Mutex.ReleaseMutex(); } public string GetImagePath(string imageSize) { var customPath = string.Empty; switch (imageSize) { case "small": customPath = WMain.Settings.UseCustomImages ? WMain.Settings.CustomImagesPathSmall : "small"; break; case "medium": customPath = WMain.Settings.UseCustomImages ? WMain.Settings.CustomImagesPathMedium : "medium"; break; case "full": if (WMain.Settings.UseCustomToolTips) customPath = WMain.Settings.CustomToolTipsPath; break; case "background": customPath = WMain.Settings.CustomBackgroundPath; break; } if (!Path.IsPathRooted(customPath)) customPath = BetterCombine(ImageRoot, "images", customPath); return customPath; } public BitmapImage GetBitmapImage(string imageName, string imageSize) { // For imageSize == "set", I use these images: // https://boardgamegeek.com/filepage/73273/dominion-card-icons-vector-images if (WMain.Settings == null) return null; if (!BitmapImageCache.ContainsKey(imageName)) BitmapImageCache[imageName] = new Dictionary(); if (!BitmapImageCache[imageName].ContainsKey(imageSize)) { var imageFilename = imageName; if (!Path.IsPathRooted(imageFilename)) imageFilename = Path.Combine(GetImagePath(imageSize), imageName.Replace(" ", "").Replace("'", "").Replace("/", "")); BitmapImageCache[imageName][imageSize] = LoadImage(imageFilename); // Fall back to the standard if we can't find the custom image if (BitmapImageCache[imageName][imageSize] == null) BitmapImageCache[imageName][imageSize] = LoadImage(BetterCombine(ImageRoot, "images", imageSize, imageName)); } var im = BitmapImageCache[imageName][imageSize]; if (im != null && im.CanFreeze && !im.IsFrozen) im.Freeze(); return im; } private static IEnumerable> Coordinate(PixelColor[,] pixels) { const int maxcount = 100000; if (pixels.GetLength(0) * pixels.GetLength(1) > maxcount) { var r = new Random(); for (var i = 0; i < maxcount; i++) yield return new Tuple(r.Next(pixels.GetLength(0)), r.Next(pixels.GetLength(1))); } else { for (var x = 0; x < pixels.GetLength(0); x++) for (var y = 0; y < pixels.GetLength(1); y++) yield return new Tuple(x, y); } } public Brush GetForegroundBrush(BitmapImage image) { var key = image.SourcePath(); if (!ForegroundBrushCache.ContainsKey(key)) { var pixels = ImageUtilities.GetPixels(image); double lSum = 0; var count = 0; foreach (var coordinate in Coordinate(pixels)) { int x = coordinate.Item1, y = coordinate.Item2; var c = Color.FromArgb(pixels[x, y].Alpha, pixels[x, y].Red, pixels[x, y].Green, pixels[x, y].Blue); var l = 0.2126 * c.ScR + 0.7152 * c.ScG + 0.0722 * c.ScB; count++; lSum += l; } lSum /= count; var value = lSum < 0.5 ? Brushes.White : Brushes.Black; if (key != string.Empty) ForegroundBrushCache[key] = value; else return value; } return ForegroundBrushCache[key]; } public static BitmapImage LoadImage(string filename) { if (IGNORED_PATTERNS.Any(p => Regex.IsMatch(filename, p))) return null; string fixedFilename; if (File.Exists(filename)) fixedFilename = filename; else if (File.Exists(filename + ".png")) fixedFilename = $"{filename}.png"; else if (File.Exists(filename + ".jpg")) fixedFilename = $"{filename}.jpg"; else if (File.Exists(filename + ".gif")) fixedFilename = $"{filename}.gif"; else return null; BitmapImage image = new BitmapImage(); var fs = new FileStream(fixedFilename, FileMode.Open); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = fs; image.EndInit(); image.Freeze(); fs.Close(); fs.Dispose(); return image; } private static string BetterCombine(params string[] pathList) { return pathList.Aggregate(string.Empty, (current, path) => current == string.Empty ? path : Path.Combine(current, path)); } } public static class BitmapImageExtension { public static string SourcePath(this BitmapImage image) { return image.UriSource != null ? image.UriSource.AbsoluteUri : (image.StreamSource is FileStream fs ? fs.Name : ""); } } }