Click here to Skip to main content
15,880,967 members
Articles / Programming Languages / C#

ImageFan

Rate me:
Please Sign up or sign in to vote.
4.76/5 (33 votes)
4 Jan 2013CPOL6 min read 75.9K   5.5K   123   23
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.

Image 1

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.

Image 2

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.

C#
using System;
using System.Threading;

namespace ImageFan.Auxiliary
{
    internal class TasksDispatcher
    {
        public TasksDispatcher(WaitCallback globalTask, WaitCallback individualTask, int tasksCount)
        {
            _globalTask = globalTask;
            _individualTask = individualTask;
            _tasksCount = tasksCount;

            _isStopped = true;
        }

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

        public void Stop()
        {
            _dispatcherIsActive = false;
        }

        public bool IsStopped
        {
            get { return _isStopped; }
        }


        #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 volatile bool _isStopped;

        private void WorkerThreadLoopMethod(object state)
        {
            try
            {
                _isStopped = false;
                
                for (var 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(i + j, j));

                    for (j = 0; (j < ProcessorCount) && (i + j < _tasksCount) && (_dispatcherIsActive); j++)
                        IndividualTaskEvents[j].Set();
                }
            }
            catch
            {
            }
            finally
            {
                _isStopped = true;
            }
        }

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

                IndividualTaskEvents[taskParam.ThreadIndex].WaitOne();
                _individualTask(taskParam.MainIndex);
            }
            catch
            {
            }
        }

        #endregion


        static TasksDispatcher()
        {
            ProcessorCount = Environment.ProcessorCount;

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

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.

Image 3

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
  • Version 0.8 - Minor code and text consistency improvements - 25/02/2015

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
GeneralMy vote of 5 Pin
D V L20-Aug-15 22:19
professionalD V L20-Aug-15 22:19 
GeneralMy vote of 5 Pin
Prasad Khandekar30-Apr-13 0:15
professionalPrasad Khandekar30-Apr-13 0:15 
GeneralMy vote of 5 Pin
deepinsd12-Mar-13 5:06
deepinsd12-Mar-13 5:06 
GeneralMy vote of 5 Pin
MarkB1235-Jan-13 0:25
MarkB1235-Jan-13 0:25 
QuestionMy vote of 5 Pin
KendoTM16-Apr-12 19:39
KendoTM16-Apr-12 19:39 
AnswerRe: My vote of 5 Pin
User 691845417-Apr-12 5:16
User 691845417-Apr-12 5:16 
GeneralMy vote of 5 Pin
Sergio Andrés Gutiérrez Rojas21-Sep-11 8:26
Sergio Andrés Gutiérrez Rojas21-Sep-11 8:26 
GeneralI like images, and I wrote something like this some time ago for my Msc Pin
Sacha Barber18-Apr-10 23:11
Sacha Barber18-Apr-10 23:11 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
User 691845419-Apr-10 0:59
User 691845419-Apr-10 0:59 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
rctaubert10-Apr-12 12:55
rctaubert10-Apr-12 12:55 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
Sacha Barber10-Apr-12 20:18
Sacha Barber10-Apr-12 20:18 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
rctaubert11-Apr-12 2:14
rctaubert11-Apr-12 2:14 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
fjdiewornncalwe4-Jan-13 10:41
professionalfjdiewornncalwe4-Jan-13 10:41 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
Sacha Barber4-Jan-13 11:48
Sacha Barber4-Jan-13 11:48 
GeneralRe: I like images, and I wrote something like this some time ago for my Msc Pin
fjdiewornncalwe7-Jan-13 3:05
professionalfjdiewornncalwe7-Jan-13 3:05 
Generalmy vote of 3 Pin
MP3Observer3-Mar-10 0:02
MP3Observer3-Mar-10 0:02 
GeneralRe: my vote of 3 PinPopular
User 69184543-Mar-10 1:36
User 69184543-Mar-10 1:36 
GeneralRe: my vote of 3 Pin
hardsoft18-Apr-10 9:07
hardsoft18-Apr-10 9:07 
GeneralRe: my vote of 3 PinPopular
Eddy Vluggen16-May-10 14:51
professionalEddy Vluggen16-May-10 14:51 
GeneralMy vote of 2 Pin
DocBrown263728-Feb-10 8:23
DocBrown263728-Feb-10 8:23 
GeneralRe: My vote of 2 PinPopular
User 69184543-Mar-10 1:42
User 69184543-Mar-10 1:42 
GeneralCode... Pin
Gary Noble24-Feb-10 6:19
Gary Noble24-Feb-10 6:19 
Please post the code here!
GeneralRe: Code... Pin
User 691845424-Feb-10 9:32
User 691845424-Feb-10 9:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.