/// <summary>
        /// Actually disposes the image in a <paramref name="container"/>.
        /// NOTE: Disposing an image might take time. So this should always be called on some low priority background worker thread.
        /// </summary>
        private void DisposeContainer(ImageContainer container)
        {
            Debug.Assert(!Monitor.IsEntered(this.cache));
            Debug.Assert(!container.IsHandleAlive);
            Debug.Assert(container.last_requesting_work_item != this.current_work_item);

            container.image?.Dispose();
            container.image = null;
        }
Beispiel #2
0
 private void HandleImageCacheDisplayItemLoaded(ImageContainer loadedImageContainer)
 {
     // The wanted image might have changed already. Even though DisplayWantedImage will check this, we can avoid
     // posting to the UI thread if we already know that it won't work.
     if (this.wantedImageHandle.Container == loadedImageContainer)
     {
         this.synchronizationContext.Post(this.displayWantedImageDelegate, null);
     }
 }
 public ImageContainer GetOrCreateContainer(FileListEntry key)
 {
     lock (this.cache)
     {
         if (!this.cache.TryGetValue(key, out ImageContainer container))
         {
             container = new ImageContainer(this, key);
             this.cache.Add(key, container);
         }
         return(container);
     }
 }
        private void LoadContainer(ImageContainer container)
        {
            Debug.Assert(container != null);
            var key = container.Key;

            Debug.Assert(key != null);
            try
            {
                var image = Util.LoadImageFromFile(key.FullPath);
                key.LastFileStatus = LastFileStatus.OK;
                container.SetImage(image);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                key.LastFileStatus = LastFileStatus.Error;
                container.SetImage(null);
            }
            Debug.Assert(container.IsLoaded);
        }
        /// <summary>
        /// Call this when the container's active handle count has reached 0.
        /// If the container is otherwise unused, it will be disposed.
        /// </summary>
        internal void DisposeContainerIfNecessary(ImageContainer container)
        {
            Debug.Assert(container != null);
            Debug.Assert(!Monitor.IsEntered(this.cache));
            Debug.Assert(!container.IsHandleAlive);
            bool needDispose = false;

            lock (this.cache)
            {
                if (container.last_requesting_work_item != this.current_work_item)
                {
                    this.cache.Remove(container.Key);
                    needDispose = true;
                }
            }

            if (needDispose)
            {
                // Dispose might actually take a long time if it was a big image.
                ThreadPool.QueueUserWorkItem(this.disposeOneContainerHelperDelegate, container);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Displays <see cref="currentDisplayIndex"/>, so this must be called after that value changes.
        /// If <see cref="currentFileList"/> is null, or if it doesn't contain the specified index, no image can be displayed.
        /// </summary>
        private void DisplayCurrent(bool scrollSelectedItemIntoView)
        {
            if (this.currentFileList == null || this.currentDisplayIndex == -1 || this.currentDisplayIndex >= this.currentFileList.Count)
            {
                // TODO this isn't ideal because it requires that displaying a file requires that it is in a browsable directory. It should be possible to display an item by its full path alone. But that makes rename extremely complicated because it would have to rename the direct display path too.
                this.overviewControl.SetDisplayIndex(-1, false);

                this.wantedImageHandle = null;
                this.DisplayWantedImage();

                this.imageCacheWorker.SetCacheWorkItem(new CacheWorkItem(null, null));
            }
            else
            {
                this.overviewControl.SetDisplayIndex(this.currentDisplayIndex, scrollSelectedItemIntoView);
                var displayFile = this.currentFileList[this.currentDisplayIndex];
                // Update window title with the current file name:
                this.UpdateWindowTitle();

                ImageContainer displayedContainer = this.imageCacheWorker.GetOrCreateContainer(displayFile);
                if (this.wantedImageHandle?.Container != displayedContainer)
                {
                    this.wantedImageHandle?.Dispose();
                    this.wantedImageHandle = displayedContainer.AddHandle();
                }

                if (this.wantedImageHandle.IsLoaded)
                {
                    // We can display this item immediately, rather than waiting for the event from the ImageCache worker.
                    this.DisplayWantedImage();
                }

                // This makes sure we preload the files around the current file.
                this.imageCacheWorker.SetCacheWorkItem(this.GetCacheWorkItemForCurrentDisplayIndex());
            }
        }
 public void Dispose()
 {
     this.Container?.RemoveHandle();
     this.Container = null;
 }
 internal ImageHandle(ImageContainer container)
 {
     Debug.Assert(container != null);
     this.Container = container;
 }
        private void CacheBuildWorkerThreadProc()
        {
            while (true)
            {
_retry:

                this.cacheWorkWait.Wait();
                CacheWorkItem item;
                lock (this.sync)
                {
                    this.cacheWorkWait.Reset();
                    item = this.cacheWorkItem;
                }

                if (item.DisplayPath == null)
                {
                    this.imageCache.PurgeAll();
                    continue;
                }

                ImageContainer displayedImageContainer = this.imageCache.GetExistingContainer(item.DisplayPath);
                // It can be null if it wasn't one of the surrounding ones from the last time, and the GUI
                // decided that it doesn't want it anymore after we started the work item.
                if (displayedImageContainer != null)
                {
                    displayedImageContainer.last_requesting_work_item = item;
                    if (!displayedImageContainer.IsLoaded)
                    {
                        this.LoadContainer(displayedImageContainer);
                    }
                }

                if (this.cacheWorkWait.IsSet)
                {
                    goto _retry;
                }

                // displayedImageContainer.Image can still be null if the file is not a valid image.
                this.DisplayItemLoaded?.Invoke(displayedImageContainer);

                foreach (var key in item.SurroundingPaths)
                {
                    if (this.cacheWorkWait.IsSet)
                    {
                        goto _retry;
                    }
                    var container = this.imageCache.GetOrCreateContainer(key);
                    container.last_requesting_work_item = item;
                    if (!container.IsLoaded)
                    {
                        this.LoadContainer(container);
                    }
                }

                if (this.cacheWorkWait.IsSet)
                {
                    goto _retry;
                }

                this.WorkItemCompleted?.Invoke();

                // Prune old containers
                // NOTE: We must do this after we have set last_requesting_work_item on each container.
                this.imageCache.PruneUnusedContainers(item);
            }
        }