/// <summary> /// Removes the given item from the cache. /// </summary> /// <param name="guid">The guid of the item to remove.</param> /// <param name="removeNow">true to remove the item now; false to remove the /// item later when the cache is purged.</param> public void Remove(Guid guid, bool removeNow) { CacheItem cacheItem = null; if (!thumbCache.TryGetValue(guid, out cacheItem)) { return; } if (removeNow) { MemoryUsed -= GetImageMemorySize(cacheItem.Size.Width, cacheItem.Size.Height); cacheItem.Dispose(); thumbCache.Remove(guid); } else { MemoryUsedByRemoved += GetImageMemorySize(cacheItem.Size.Width, cacheItem.Size.Height); removedItems.Add(guid); Purge(); } // Remove from disk cache ImageListViewItem item = null; if (mImageListView != null && mImageListView.Items.TryGetValue(guid, out item)) { string diskCacheKey = item.Adaptor.GetUniqueIdentifier(item.VirtualItemKey, cacheItem.Size, cacheItem.UseEmbeddedThumbnails, cacheItem.AutoRotate, cacheItem.UseWIC); diskCache.Remove(diskCacheKey); } }
/// <summary> /// Handles the RunWorkerCompleted event of the queued background worker. /// [IG_CHANGE] Issue #359: thread collision? leftover context? failed to check null /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="ImageGlass.ImageListView.QueuedWorkerCompletedEventArgs"/> /// instance containing the event data.</param> void bw_RunWorkerCompleted(object sender, QueuedWorkerCompletedEventArgs e) { CacheItem result = e.Result as CacheItem; // Add to cache if (result != null) { // We are done processing bool removedValue; var removed = processing.TryRemove(result.Extension, out removedValue); if (!removed) { throw new InvalidOperationException("processing.TryRemove failed"); } CacheItem existing = null; if (shellCache.TryGetValue(result.Extension, out existing)) { existing.Dispose(); shellCache.Remove(result.Extension); } shellCache.Add(result.Extension, result); } // Refresh the control lazily if (result != null && mImageListView != null) { mImageListView.Refresh(false, true); } }
/// <summary> /// Handles the RunWorkerCompleted event of the queued background worker. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="Manina.Windows.Forms.QueuedWorkerCompletedEventArgs"/> /// instance containing the event data.</param> void bw_RunWorkerCompleted(object sender, QueuedWorkerCompletedEventArgs e) { CacheItem result = e.Result as CacheItem; // We are done processing processing.Remove(result.Extension); // Add to cache if (result != null) { CacheItem existing = null; if (shellCache.TryGetValue(result.Extension, out existing)) { existing.Dispose(); shellCache.Remove(result.Extension); } shellCache.Add(result.Extension, result); } // Refresh the control lazily if (result != null && mImageListView != null) { mImageListView.Refresh(false, true); } }
private static void OnProxyDisposed(CacheItemProxy cacheItemProxy) { ReferenceCountedObjectWrapper <CacheItem> wrapper = cacheItemProxy.Wrapper; if (!wrapper.IsReferenceCountAboveZero()) { lock (_syncLock) { //The count could have gone back up if (wrapper.IsReferenceCountAboveZero()) { return; } CacheItem cacheItem = wrapper.Item; _cache.Remove(cacheItem.Key); cacheItem.Dispose(); if (_cache.Count == 0) { Trace.WriteLine("The composed lut cache is empty.", "LUT"); } } } }
/// <summary> /// Adds a virtual item to the cache queue. /// </summary> /// <param name="guid">The guid representing this item.</param> /// <param name="key">The key of this item.</param> /// <param name="thumbSize">Requested thumbnail size.</param> /// <param name="useEmbeddedThumbnails">UseEmbeddedThumbnails property of the owner control.</param> public void Add(Guid guid, object key, Size thumbSize, UseEmbeddedThumbnails useEmbeddedThumbnails) { lock (lockObject) { // Already cached? CacheItem item = null; if (thumbCache.TryGetValue(guid, out item)) { if (item.Size == thumbSize && item.UseEmbeddedThumbnails == useEmbeddedThumbnails) { return; } else { item.Dispose(); thumbCache.Remove(guid); } } // Add to cache toCache.Push(new CacheItem(guid, key, thumbSize, null, CacheState.Unknown, useEmbeddedThumbnails)); Monitor.Pulse(lockObject); } }
private void RemoveAt(int index) { CacheItem item = FPages[index]; FPages.RemoveAt(index); item.Dispose(); }
/// <summary> /// Purges removed items from the cache. /// </summary> /// <param name="force">true to purge the cache now, regardless of /// memory usage; otherwise false to automatically purge the cache /// depending on memory usage.</param> public void Purge(bool force) { // Remove items now if we can free more than 25% of the cache limit if (force || IsPurgeNeeded()) { foreach (Guid guid in removedItems) { CacheItem item = null; if (thumbCache.TryGetValue(guid, out item)) { item.Dispose(); thumbCache.Remove(guid); } if (galleryItem != null && galleryItem.Guid == guid) { galleryItem.Dispose(); galleryItem = null; } } removedItems.Clear(); MemoryUsed -= MemoryUsedByRemoved; MemoryUsedByRemoved = 0; } }
private void RemoveAt(int index) { CacheItem item = pages[index]; item.Dispose(); item = null; pages.RemoveAt(index); }
/// <summary> /// Ends editing an item. After this call, item /// image will be continued to be fetched from the /// file. /// </summary> /// <param name="guid">The guid representing the item.</param> public void EndItemEdit(Guid guid) { lock (lockObject) { if (editCache.ContainsKey(guid)) { editCache[guid].Dispose(); editCache.Remove(guid); } if (rendererGuid == guid) { rendererGuid = Guid.Empty; if (rendererItem != null) { rendererItem.Dispose(); } } } }
/// <summary> /// Adds a virtual item to the cache. /// </summary> /// <param name="guid">The guid representing this item.</param> /// <param name="key">The key of this item.</param> /// <param name="thumbSize">Requested thumbnail size.</param> /// <param name="thumb">Thumbnail image to add to cache.</param> /// <param name="useEmbeddedThumbnails">UseEmbeddedThumbnails property of the owner control.</param> public void Add(Guid guid, object key, Size thumbSize, Image thumb, UseEmbeddedThumbnails useEmbeddedThumbnails) { lock (lockObject) { // Already cached? CacheItem item = null; if (thumbCache.TryGetValue(guid, out item)) { if (item.Size == thumbSize && item.UseEmbeddedThumbnails == useEmbeddedThumbnails) { return; } else { item.Dispose(); thumbCache.Remove(guid); } } // Add to cache thumbCache.Add(guid, new CacheItem(guid, key, thumbSize, thumb, CacheState.Cached, useEmbeddedThumbnails)); } try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { mImageListView.Invoke(new ThumbnailCachedEventHandlerInternal( mImageListView.OnThumbnailCachedInternal), guid, false); mImageListView.Invoke(new RefreshDelegateInternal( mImageListView.OnRefreshInternal)); } } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } }
/// <summary> /// Removes the given item from the cache. /// </summary> /// <param name="extension">File extension.</param> public void Remove(string extension) { if (string.IsNullOrEmpty(extension)) { throw new ArgumentException("extension cannot be null", "extension"); } CacheItem item = null; if (shellCache.TryGetValue(extension, out item)) { item.Dispose(); shellCache.Remove(extension); } }
private void Delete(CacheItem item) { if (item.Value != null) { string[] itemKeys = item.Keys; item.Dispose(); _expirations.RemoveExpiration(item); // Remove the lookup by this key to this item. foreach (string itemKey in itemKeys) { _cache.Remove(itemKey); } } }
/// <summary> /// Removes the given item from the cache. /// </summary> /// <param name="guid">The guid of the item to remove.</param> /// <param name="removeNow">true to remove the item now; false to remove the /// item later when the cache is purged.</param> public void Remove(Guid guid, bool removeNow) { lock (lockObject) { CacheItem item = null; if (!thumbCache.TryGetValue(guid, out item)) { return; } if (removeNow) { memoryUsed -= item.Size.Width * item.Size.Height * 24 / 8; item.Dispose(); thumbCache.Remove(guid); } else { // Calculate the memory usage (approx. Width * Height * BitsPerPixel / 8) memoryUsedByRemoved += item.Size.Width * item.Size.Height * 24 / 8; removedItems.Add(guid); // Remove items now if we can free more than 25% of the cache limit if ((mCacheLimitAsMemory != 0 && memoryUsedByRemoved > mCacheLimitAsMemory / 4) || (mCacheLimitAsItemCount != 0 && removedItems.Count > mCacheLimitAsItemCount / 4)) { CacheItem itemToRemove = null; foreach (Guid iguid in removedItems) { if (thumbCache.TryGetValue(iguid, out itemToRemove)) { itemToRemove.Dispose(); thumbCache.Remove(iguid); } } removedItems.Clear(); memoryUsed -= memoryUsedByRemoved; memoryUsedByRemoved = 0; } } } }
/// <summary> /// Removes the given item from the cache. /// </summary> /// <param name="guid">The guid of the item to remove.</param> /// <param name="removeNow">true to remove the item now; false to remove the /// item later when the cache is purged.</param> public void Remove(Guid guid, bool removeNow) { CacheItem item = null; if (!thumbCache.TryGetValue(guid, out item)) { return; } if (removeNow) { MemoryUsed -= GetImageMemorySize(item.Size.Width, item.Size.Height); item.Dispose(); thumbCache.Remove(guid); } else { MemoryUsedByRemoved += GetImageMemorySize(item.Size.Width, item.Size.Height); removedItems.Add(guid); Purge(); } }
/// <summary> /// Used by the worker thread to generate image thumbnails. /// Once a thumbnail image is generated, the item will be redrawn /// to replace the placeholder image. /// </summary> private void DoWork() { System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); while (!Stopping) { Guid guid = new Guid(); CacheItem request = null; bool rendererRequest = false; lock (lockObject) { // Wait until we have items waiting to be cached if (toCache.Count == 0 && rendererToCache.Count == 0) { Monitor.Wait(lockObject); } } // Set to true when we exceed the cache memory limit bool cleanupRequired = false; // Set to true when we fetch at least one thumbnail bool thumbnailCreated = false; // Loop until we exhaust the queue bool queueFull = true; while (queueFull && !Stopping) { lock (lockObject) { sw.Start(); // Get an item from the queue if (toCache.Count != 0) { request = toCache.Pop(); guid = request.Guid; // Is it already cached? CacheItem existing = null; if (thumbCache.TryGetValue(guid, out existing)) { if (existing.Size == request.Size) { request = null; } } } else if (rendererToCache.Count != 0) { request = rendererToCache.Pop(); guid = request.Guid; rendererToCache.Clear(); rendererRequest = true; } } // Is it outside visible area? bool isvisible = true; if (request != null && mCacheMode == CacheMode.OnDemand) { try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { isvisible = (bool)mImageListView.Invoke(new CheckItemVisibleDelegateInternal( mImageListView.IsItemVisible), guid); } } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } } lock (lockObject) { if (!rendererRequest && !isvisible) { request = null; } } // Proceed if we have a valid request CacheItem result = null; if (request != null) { Image thumb = null; // Is it in the edit cache? Image editSource = null; lock (lockObject) { if (!editCache.TryGetValue(guid, out editSource)) { editSource = null; } } if (editSource != null) { thumb = Utility.ThumbnailFromImage(editSource, request.Size); } // Read thumbnail image if (thumb == null) { if (request.IsVirtualItem) { VirtualItemThumbnailEventArgs e = new VirtualItemThumbnailEventArgs( request.VirtualItemKey, request.Size); if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { mImageListView.RetrieveVirtualItemThumbnailInternal(e); } if (e.ThumbnailImage != null) { thumb = e.ThumbnailImage; } } else { thumb = Utility.ThumbnailFromFile(request.FileName, request.Size, request.UseEmbeddedThumbnails); } } // Create the cache item if (thumb == null) { if (!mRetryOnError) { result = new CacheItem(guid, request.FileName, request.Size, null, CacheState.Error, request.UseEmbeddedThumbnails); } else { result = null; } } else { result = new CacheItem(guid, request.FileName, request.Size, thumb, CacheState.Cached, request.UseEmbeddedThumbnails); thumbnailCreated = true; } if (result != null) { if (rendererRequest) { lock (lockObject) { if (rendererItem != null) { rendererItem.Dispose(); } rendererGuid = guid; rendererItem = result; rendererRequest = false; } } else { lock (lockObject) { CacheItem existing = null; if (thumbCache.TryGetValue(guid, out existing)) { existing.Dispose(); thumbCache.Remove(guid); } thumbCache.Add(guid, result); if (thumb != null) { // Did we exceed the cache limit? memoryUsed += thumb.Width * thumb.Height * 24 / 8; if ((mCacheLimitAsMemory != 0 && memoryUsed > mCacheLimitAsMemory) || (mCacheLimitAsItemCount != 0 && thumbCache.Count > mCacheLimitAsItemCount)) { cleanupRequired = true; } } } } } try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { mImageListView.Invoke(new ThumbnailCachedEventHandlerInternal( mImageListView.OnThumbnailCachedInternal), guid, (result == null)); } } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } } // Check if the cache is exhausted lock (lockObject) { if (toCache.Count == 0 && rendererToCache.Count == 0) { queueFull = false; } } // Do we need a refresh? sw.Stop(); if (sw.ElapsedMilliseconds > 100) { try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { mImageListView.Invoke(new RefreshDelegateInternal( mImageListView.OnRefreshInternal)); } sw.Reset(); } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } } if (queueFull) { sw.Start(); } else { sw.Reset(); sw.Stop(); } } // Clean up invisible items if (cleanupRequired) { Dictionary <Guid, bool> visible = new Dictionary <Guid, bool>(); try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { visible = (Dictionary <Guid, bool>)mImageListView.Invoke( new GetVisibleItemsDelegateInternal(mImageListView.GetVisibleItems)); } } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } if (visible.Count != 0) { lock (lockObject) { foreach (KeyValuePair <Guid, CacheItem> item in thumbCache) { if (!visible.ContainsKey(item.Key) && item.Value.State == CacheState.Cached && item.Value.Image != null) { removedItems.Add(item.Key); memoryUsedByRemoved += item.Value.Image.Width * item.Value.Image.Width * 24 / 8; } } foreach (Guid iguid in removedItems) { if (thumbCache.ContainsKey(iguid)) { thumbCache[iguid].Dispose(); thumbCache.Remove(iguid); } } removedItems.Clear(); memoryUsed -= memoryUsedByRemoved; memoryUsedByRemoved = 0; } } } if (thumbnailCreated) { try { if (mImageListView != null && mImageListView.IsHandleCreated && !mImageListView.IsDisposed) { mImageListView.Invoke(new RefreshDelegateInternal(mImageListView.OnRefreshInternal)); } } catch (ObjectDisposedException) { if (!Stopping) { throw; } } catch (InvalidOperationException) { if (!Stopping) { throw; } } } } lock (lockObject) { stopped = true; } }
/// <summary> /// Handles the RunWorkerCompleted event of the queued background worker. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="ImageGlass.ImageListView.QueuedWorkerCompletedEventArgs"/> /// instance containing the event data.</param> void bw_RunWorkerCompleted(object sender, QueuedWorkerCompletedEventArgs e) { CacheRequest request = e.UserState as CacheRequest; CacheItem result = e.Result as CacheItem; // We are done processing if (request.RequestType == RequestType.Renderer) { processingRendererItem = Guid.Empty; } else if (request.RequestType == RequestType.Gallery) { processingGalleryItem = Guid.Empty; } else { processing.Remove(request.Guid); } // Do not process the result if the cache operation // was cancelled. if (e.Cancelled) { return; } // Dispose old item and add to cache if (request.RequestType == RequestType.Renderer) { if (rendererItem != null) { rendererItem.Dispose(); } rendererItem = result; } else if (request.RequestType == RequestType.Gallery) { if (galleryItem != null) { galleryItem.Dispose(); } galleryItem = result; } else if (result != null) { CacheItem existing = null; if (thumbCache.TryGetValue(result.Guid, out existing)) { existing.Dispose(); thumbCache.Remove(result.Guid); } thumbCache.Add(result.Guid, result); if (result.Image != null) { // Did the thumbnail size change while we were // creating the thumbnail? if (result.Size != mImageListView.ThumbnailSize) { result.State = CacheState.Unknown; } // Purge invisible items if we exceeded the cache limit MemoryUsed += GetImageMemorySize(result.Image); if (IsCacheLimitExceeded()) { PurgeInvisible(true); } } } //Refresh the control if (mImageListView != null) { if (request.RequestType != RequestType.Thumbnail || mImageListView.IsItemVisible(request.Guid)) { mImageListView.Refresh(false, true); } } // Raise the ThumbnailCached event if (mImageListView != null) { mImageListView.OnThumbnailCachedInternal(result.Guid, result.Image, result.Size, request.RequestType == RequestType.Thumbnail); } // Raise the CacheError event if (e.Error != null && mImageListView != null) { mImageListView.OnCacheErrorInternal(result.Guid, e.Error, CacheThread.Thumbnail); } }