/// <summary> /// Raises the RetrieveVirtualItem event. /// </summary> /// <param name="e">A VirtualItemThumbnailEventArgs that contains event data.</param> protected virtual void OnRetrieveVirtualItemThumbnail(VirtualItemThumbnailEventArgs e) { if (RetrieveVirtualItemThumbnail != null) RetrieveVirtualItemThumbnail(this, e); }
/// <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 thumbCache.Remove(guid); } } 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, Color.White); // 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, Color.White); } } // 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) { 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> /// Raises the RetrieveVirtualItem event. /// This method is invoked from the thumbnail thread. /// </summary> /// <param name="e">A VirtualItemThumbnailEventArgs that contains event data.</param> internal virtual void RetrieveVirtualItemThumbnailInternal(VirtualItemThumbnailEventArgs e) { OnRetrieveVirtualItemThumbnail(e); }
/// <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 { thumbCache.Remove(guid); } } } 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, Color.White); } // 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, Color.White); } } // 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) { 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; } }