Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ImageFan

0.00/5 (No votes)
4 Jan 2013 1  
A lightweight image viewer for .NET 2.0, supporting multi-core processing

Introduction

ImageFan is a lightweight image viewer for .NET 2.0, supporting multi-core processing. At the moment it is in beta stage and stable on the implemented functionality.

 

Background

 

I have always wanted to exploit the capabilities of .NET to create an image viewer offering a managed (possibly portable due to Mono) counterpart to the existing C / C++ solutions. Also, the lack of 64 bit image viewers has enticed me to pursue a .NET solution that would implicitly run as a 64 bit application, hardware and software platform permitting.

Visual Layout and Functionality

The application is designed in the traditional style of contemporary image viewers as follows:

  • A drives list and corresponding directories tree on the left-hand side of the window
  • A thumbnails list on the right-hand side, revealing thumbnails to the images contained in the selected drive and directory

Selecting a drive refreshes the list of directories and reveals the thumbnails of the images contained on the root drive. Selecting a specific directory fills the thumbnails list with the appropriate thumbnails. The thumbnails list can be navigated on by using the arrow keys and the scroll bar.

When the left mouse button is clicked or the Enter key pressed, while on a thumbnail, a new window is opened, containing the image in full size, scrollable if at least one dimension of the image is greater than the corresponding screen size dimension. The user can traverse the images list using this window, by employing the Space and Backspace keys and the arrow keys. The window can be closed by pressing the Esc key or the closing button on the window.

If the image window is clicked on again or pressed Enter upon, it will set the image to full screen view, resizing it if necessary to fit on the screen. In the full screen mode, the thumbnails list is navigable with the Space and Backspace keys, the arrow keys and the mouse wheel. The user can exit this mode by pressing the Esc key or the left mouse button.

Implementation Challenges and Constraints

The project structure is revealed in the diagram below. I will explain each relevant source code artifact in turn.

 

Class TasksDispatcher 

 

This class is a general tasks dispatcher that partitions an array of tasks on the number of available processor cores. In the previous versions, the class used raw threads. In this release, it relies on the ThreadPool class of .NET and, thus, on pooled threads, minimizing thread creation and destruction costs.

For the problem at hand, I use the tasks dispatcher class to partition (by their indexes) the collection of image thumbnails, which is to be displayed asynchronously when the user selects a directory.

namespace ImageFan
{
    class TasksDispatcher
    {

        static TasksDispatcher()
        {
            ProcessorCount = Environment.ProcessorCount;

            IndividualTaskEvents = new AutoResetEvent[ProcessorCount];
            for (int i = 0; i < ProcessorCount; i++)
                IndividualTaskEvents[i] = new AutoResetEvent(false);
        }


        public TasksDispatcher(WaitCallback globalTask, WaitCallback individualTask, int tasksCount)
        {
            this.globalTask = globalTask;
            this.individualTask = individualTask;
            this.tasksCount = tasksCount;
        }


        public void Start()
        {
            dispatcherIsActive = true;
            
            ThreadPool.QueueUserWorkItem(WorkerThreadLoopMethod);
        }


        public void Stop()
        {
            dispatcherIsActive = false;
        }


        #region Private

        private static readonly int ProcessorCount;
        private static AutoResetEvent[] IndividualTaskEvents;

        private WaitCallback globalTask;
        private WaitCallback individualTask;
        private int tasksCount;

        private volatile bool dispatcherIsActive;


        private void WorkerThreadLoopMethod(object state)
        {
            try
            {
                for (int i = 0; (i < tasksCount) && (dispatcherIsActive); i += ProcessorCount)
                {
                    int j;

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount) && (dispatcherIsActive); j++)
                        globalTask(i + j);

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount) && (dispatcherIsActive); j++)
                        ThreadPool.QueueUserWorkItem(IndividualTaskWrapper, new TaskParameter() { mainIndex = i + j, threadIndex = j });

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount) && (dispatcherIsActive); j++)
                        IndividualTaskEvents[j].Set();
                }
            }
            catch
            {
            }
        }

        private void IndividualTaskWrapper(object state)
        {
            try
            {
                TaskParameter taskParam = ((TaskParameter)state);

                IndividualTaskEvents[taskParam.threadIndex].WaitOne();
                individualTask(taskParam.mainIndex);
            }
            catch
            {
            }
        }

        #endregion

    }

}

Class FolderTreeView

This class is a Windows Form custom control, extending the TreeView control. It displays the folders (directories) on the selected drive in a tree-like manner and, when a directory node is selected, triggers the display of thumbnails in the ThumbnailsSequence control.

Class ThumbnailBox

This type is a Windows Form custom control, inheriting from the UserControl class. It is a variable-size control that displays an image thumbnail box, containing the image thumbnail itself, decorated with the image file name. Its largest dimension, whether width or height, is scaled to ThumbnailSize (200 pixels), while the other dimension is scaled proportionally.

Class ThumbnailsSequence

The class ThumbnailsSequence is derived from a FlowLayout panel and used as a container for the thumbnails generated for the selected directory on the disc.

Class ImageForm

ImageForm is a Windows Form that is shown as a dialog, when the user clicks on a particular image thumbnail, as the current image, when keyboard-navigating on the images inside the directory, or after escaping the full screen mode. The form preserves the image size, enabling scrolling in case the image exceeds the screen size.

Class FullScreenImage

This class is a Windows Form shown as a dialog without a Form border, occupying the full screen size and having a black background. As such, it gives the optical illusion of being an unusual GUI artifact featuring a full screen mode. It also resizes the image, if it is larger than the full screen dimensions available, while maintaining the initial aspect ratio.

Class ImageFile

ImageFile is a flyweight-pattern class, having the image thumbnail stored as its intrinsic state and the full-sized image as its extrinsic state, retrieved only on demand.

Class ImageFolder

This class extracts and manages the image files within a given folder (directory).

Class ImageResizer

This type features two operations: resizing an image extracted from a file (for the generation of thumbnails) and resizing an image taken from memory (for the full-screen mode).

Class GlobalData

This class contains references to the resource images LoadingImage and InvalidImage, as well as to their respective thumbnails.

Lessons Learned

Although this image viewer is a managed application, there is no disparity in browsing or viewing speed (besides specific optimizations) between this solution and native executable ones, such as IrfanView, XnView and AcdSee. This is because the .NET System.Drawing and System.Windows.Forms classes are rather tight wrappers over the WinAPI functionality.

The Image class, inherited by the Bitmap class, implements the IDisposable interface, making the flyweight design pattern implementation straightforward, due to being able to manage the image memory footprint in a deterministic manner.

The improved presence of 64 bit operating systems makes managed programming runtimes truly shine. This is because the applications written in managed languages (such as .NET CLS compliant ones) inherently support a 32 to 64 bit switch using the same binary package, while offering the full advantages of the running platform.

Source Code and Application Download

The complete source code of the ImageFan application (a Google Code project) can be accessed here. If one is only interested in the binaries, they can be downloaded from this link.

I would gladly welcome contributions and feedback to this ImageFan open-source (GPL v3) project.

References

  • [1] The Microsoft Developer Network (MSDN) pages

History

  • Version 0.1 - Initial submission - 24/02/2010
  • Version 0.2 - Added download links at the head of the article - 28/02/2010
  • Version 0.3 - Code optimizations and bug-fixes - 18/04/2010
  • Version 0.4 - Updated content, sources and binaries - 21/09/2011
  • Version 0.5 - Reengineered code with significant bug fixes - 09/04/2012 
  • Version 0.6 - Many code changes and enhancements. Removed some of the code samples from the article, as they have become bloated and, thus, distracting. Cleaned up the article text - 04/01/2013
  • Version 0.7 - Some bug fixes. Made the thumbnail box variable-size to increase the number of thumbnails on the screen - 09/04/2013  

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here