public Bitmap GetResizedBitmap(string path, out int originalWidth, out int originalHeight) { if (!FilenameUtils.LooksLikeImage(path) || !File.Exists(path)) { originalWidth = 0; originalHeight = 0; return(new Bitmap(1, 1, PixelFormat.Format32bppPArgb)); } Bitmap bitmapFull = GetBitmap(path, _shouldRotateThisImage, out bool bitmapWillLockFile); // resize and preserve ratio originalWidth = bitmapFull.Width; originalHeight = bitmapFull.Height; if (bitmapFull.Width > MaxWidth || bitmapFull.Height > MaxHeight) { using (bitmapFull) { var ratio = Math.Min((double)MaxWidth / bitmapFull.Width, (double)MaxHeight / bitmapFull.Height); int newWidth = (int)(bitmapFull.Width * ratio); int newHeight = (int)(bitmapFull.Height * ratio); return(ClassImageOps.ResizeImage(bitmapFull, newWidth, newHeight, this.ResizeToFit, path)); } } else if (bitmapWillLockFile) { // make a copy of the bitmap, otherwise the file remains locked using (bitmapFull) { return(new Bitmap(bitmapFull)); } } else { return(bitmapFull); } }
// add paths to cache, and then checks for images to remove. public void Add(string[] paths) { bool checkIfTooManyInCache = false; foreach (var path in paths) { if (path == null) { continue; } lock (_lock) { // skip if it's already in the cache if (_list.SearchForUpToDateCacheEntry(path) != -1) { continue; } // reading and resizing the bitmap can be done outside the lock, // but this might do redundant work. var bitmap = GetResizedBitmap(path, out int originalWidth, out int originalHeight); DateTime lastmod = DateTime.MinValue; long filesize = 0; if (File.Exists(path)) { var fileInfo = new FileInfo(path); lastmod = fileInfo.LastWriteTimeUtc; filesize = fileInfo.Length; } bitmap = ClassImageOps.ResizeImageByFactor(bitmap, ResizeFactor, VerticalScrollFactor); bitmap = ClassImageOps.TileImage(bitmap, _tileImages, MaxWidth, MaxHeight); _list.Add(new CacheEntry( path, bitmap, originalWidth, originalHeight, lastmod, filesize)); checkIfTooManyInCache = _list.Count > _cacheSize; } } if (checkIfTooManyInCache) { // ask form before we call Dispose() in case form is currently using this image. _callbackOnUiThread.Invoke(new Action(() => { lock (_lock) { // it's possible there is no work left to do, due to another thread. // iterate backwards, since RemoveAt repositions subsequent elements var howManyToRemove = _list.Count - _cacheSize; for (int i = howManyToRemove - 1; i >= 0; i--) { if (i <= _list.Count - 1 && _canDisposeBitmap(_list[i].Image)) { _list.RemoveAt(i); } } } })); } }