Imaging.NET Source Code - Pixel/Image Sample

The central component of the Imaging.NET application is the Image class. It relies heavily on the Pixel class to perform all image processing operations in the application.  All source code markup was produced by the excellent CopySourceAsHtml Visual Studio 2005 Add-In.


    1 using System;
    2 using System.Drawing;
    3 using System.Drawing.Drawing2D;
    4 using System.IO;
    5 using System.Collections;
    6 using Microsoft.VisualBasic;
    7 using System.Windows.Forms;
    8 
    9 
   10 //////////////////////////////////////////////////////////////////////////////////////////////
   11 //// Pixel Class
   12 //////////////////////////////////////////////////////////////////////////////////////////////
   13 //// For purposes of this project, 
   14 //// a Pixel consists of i (row) and j (column) coordinates only.
   15 ////
   16 //// NOTE: a Pixel (and/or its neighbors) can have negative coordinates
   17 ////
   18 public class Pixel : IComparable, ICloneable
   19 {
   20 
   21     public int i;
   22     public int j;
   23 
   24     public Pixel(int new_i, int new_j)
   25     {
   26         i = new_i;
   27         j = new_j;
   28     }
   29 
   30     public Pixel(Pixel pixel)
   31     {
   32         i = pixel.i;
   33         j = pixel.j;
   34     }
   35 
   36     //// Gets the specified 8 neighbor of this pixel //
   37     public Pixel Neighbor(int index)
   38     {
   39         int[] di = {0, -1, -1, -1, 0, 1, 1, 1};
   40         int[] dj = {-1, -1, 0, 1, 1, 1, 0, -1};
   41 
   42         return new Pixel(i + di[index], j + dj[index]);
   43     }
   44 
   45 
   46     //// Returns a collection of the 4 neighbors of this pixel //
   47     public PixelCollectionList Get4Neighbors()
   48     {
   49         PixelCollectionList result;
   50 
   51         result = new PixelCollectionList();
   52 
   53         result.Add(Neighbor(0));
   54         result.Add(Neighbor(2));
   55         result.Add(Neighbor(4));
   56         result.Add(Neighbor(6));
   57 
   58         return result;
   59     }
   60 
   61     //// Returns a collection of the 8 neighbors of this pixel //
   62     public PixelCollectionList Get8Neighbors()
   63     {
   64         PixelCollectionList result;
   65 
   66         result = new PixelCollectionList();
   67         result.Add(Neighbor(0));
   68         result.Add(Neighbor(1));
   69         result.Add(Neighbor(2));
   70         result.Add(Neighbor(3));
   71         result.Add(Neighbor(4));
   72         result.Add(Neighbor(5));
   73         result.Add(Neighbor(6));
   74         result.Add(Neighbor(7));
   75 
   76         return result;
   77     }
   78 
   79 
   80     //// Key for comparing pixels //
   81     public int SortingKey {
   82         get {
   83             const int INDEX_MULT = 10000;
   84 
   85             //// Return a unique index for this pixel (in a 10000 x 10000 matrix) //
   86             return (i * INDEX_MULT) + j;
   87         }
   88     }
   89 
   90     public int ArrayIndex(int width)
   91     {
   92         return (i * width) + j;
   93     }
   94 
   95 
   96     //// Compare pixels //
   97     public bool Equals(Pixel oRef)
   98     {
   99         //// Pixels are equal if their sorting keys are equal //
  100         return SortingKey == oRef.SortingKey;
  101     }
  102 
  103 
  104     //// Compare two pixels
  105     //// IComparable interface requires this function //
  106     //// It returns -1 if less, 0 if equal, 1 if more
  107     int IComparable.CompareTo(object obj)
  108     {
  109         int sortingKey1;
  110         int sortingKey2;
  111 
  112         //// Prepare to compare sorting keys //
  113         sortingKey1 = SortingKey;
  114         sortingKey2 = ((Pixel)obj).SortingKey;
  115 
  116         if (sortingKey1 < sortingKey2)
  117         {
  118             return -1;
  119         }
  120 
  121         if (sortingKey1 == sortingKey2)
  122         {
  123             return 0;
  124         }
  125 
  126         return 1;
  127     }
  128 
  129     //// Make a copy of this pixel
  130     object ICloneable.Clone()
  131     {
  132         return new Pixel(this);
  133     }
  134 }
  135 
  136 
  137 
  138 //////////////////////////////////////////////////////////////////////////////////////////////
  139 //// Image Class
  140 //////////////////////////////////////////////////////////////////////////////////////////////
  141 //// This class contains all image processing code in the application //
  142 ////
  143 //// It contains displayable bitmap objects
  144 //// as well as color, type, and label information for every pixel in the image.
  145 ////
  146 //// Methods are provided to transform, filter, and manipulate image data
  147 //// An enumerator class enables this object to be treated as a collection of pixels.
  148 //// (For each pixel in Me)
  149 ////
  150 //// Two bitmap objects are provided, one with raw pixel color data
  151 //// and another for "color labels"...
  152 //// color labels are used when the font size for displaying text labels 
  153 //// would be too small to see.
  154 ////
  155 //////////////////////////////////////////////////////////////////////////////////////////////
  156 ////
  157 
  158 public class Image : ICloneable, IEnumerable, ICollection
  159 {
  160     //// Every pixel is either a background, foreground, or a boundary pixel //
  161     public enum PixelTypes
  162     {
  163         Background = 0,
  164         Foreground = 1,
  165         Boundary = 2
  166     }
  167 
  168     public enum ColorLabelStates
  169     {
  170         Enabled = 1,
  171         Disabled = 0
  172     }
  173 
  174     public enum AnimationStates
  175     {
  176         Enabled = 1,
  177         Disabled = 0
  178     }
  179 
  180     public class PixelIndexedProperty<TypeT>
  181     {
  182         Image _image;
  183         TypeT[] moArray;
  184 
  185         public PixelIndexedProperty(TypeT[] array, Image image)
  186         {
  187             moArray = array;
  188             _image = image;
  189         }
  190 
  191         public TypeT this[Pixel pixel]
  192         {
  193             get
  194             {
  195                 return moArray[pixel.ArrayIndex(_image._width)];
  196             }
  197             set
  198             {
  199                 int i;
  200                 int j;
  201 
  202                 i = pixel.i;
  203                 j = pixel.j;
  204 
  205                 moArray[(i * _image._width) + j] = value;
  206                 if (value is System.Drawing.Color)
  207                 {
  208                     _image._bitmap.SetPixel(j, i, (System.Drawing.Color)(object)value);
  209                     if (_image._colorLabels == ColorLabelStates.Enabled)
  210                     {
  211                         _image._bitmapWithColorLabels.SetPixel(j, i, (System.Drawing.Color)(object)value);
  212                     }
  213                 }
  214 
  215                 _image.AnimationRaisePixelChanged(pixel);
  216             }
  217         }
  218     }
  219 
  220     //////////////////////////////////////////////////////////////////////////////////////////////
  221     //// PRIVATE DATA MEMBERS
  222     //////////////////////////////////////////////////////////////////////////////////////////////
  223 
  224     //// Big number for marking pixels during processing
  225     const int PIXEL_LABEL_MARK = 2000000000;
  226 
  227     //// The height of the contained image (in pixels)
  228     protected int _height;
  229     //// The width of the contained image (in pixels)
  230     protected int _width;
  231     protected int _count;
  232     //// The background color to apply to new images
  233     protected System.Drawing.Color _backColor;
  234 
  235     //// Indicates if color labels are enabled (see class description)
  236     protected ColorLabelStates _colorLabels;
  237 
  238     //// Image to display to user
  239     protected System.Drawing.Bitmap _bitmap;
  240     //// Image to display to user (when color labels are needed)
  241     protected System.Drawing.Bitmap _bitmapWithColorLabels;
  242 
  243     //// Array ofSystem.Drawing.colors (for faster access to color data)
  244     protected System.Drawing.Color[] _pixelColorArray;
  245     //// Array of pixel types (background, foreground, border, etc)
  246     protected PixelTypes[] _pixelTypeArray;
  247     //// Array of (numeric) pixel labels.
  248     protected int[] _pixelLabelArray;
  249 
  250     //// Invalidates collection enumerator when underlying image size changes
  251     protected int lastModified;
  252 
  253     protected AnimationStates _animation;
  254     protected bool _animationState;
  255     protected System.Collections.Stack _animationStateStack;
  256     protected Pixel _animationLastScannedPixel;
  257     protected int _progressMin;
  258     protected int _progressMax;
  259     protected int _progressCount;
  260 
  261     public bool CancelOperation;
  262 
  263     protected PixelIndexedProperty<System.Drawing.Color> PixelColorProperty;
  264     protected PixelIndexedProperty<PixelTypes> PixelTypeProperty;
  265     protected PixelIndexedProperty<int> PixelLabelProperty;
  266 
  267 
  268 
  269     //////////////////////////////////////////////////////////////////////////////////////////////
  270     //// PUBLIC DELEGATES
  271     //////////////////////////////////////////////////////////////////////////////////////////////
  272 
  273     //// Events for animation, progress bar, and status changes //
  274     public event AnimationPixelScannedEventHandler AnimationPixelScanned;
  275     public delegate void AnimationPixelScannedEventHandler(Pixel pixel, Pixel lastScannedPixel);
  276     public event AnimationPixelChangedEventHandler AnimationPixelChanged;
  277     public delegate void AnimationPixelChangedEventHandler(Pixel pixel);
  278     public event AnimationImageChangedEventHandler AnimationImageChanged;
  279     public delegate void AnimationImageChangedEventHandler();
  280     public event ProgressChangedEventHandler ProgressChanged;
  281     public delegate void ProgressChangedEventHandler(int progressMin, int progressMax, int progressCount, bool refreshImage);
  282     public event StatusChangedEventHandler StatusChanged;
  283     public delegate void StatusChangedEventHandler(string text);
  284 
  285     //// Delegate (function pointer) for transforming an image (called for every pixel during transform).
  286     public delegate void PixelTransformDelegate(Image image, Pixel pixel, PixelTransformOptions transformOptions);
  287 
  288 
  289     //////////////////////////////////////////////////////////////////////////////////////////////
  290     //// CONSTRUCTORS
  291     //////////////////////////////////////////////////////////////////////////////////////////////
  292 
  293     public Image(int height, int width, System.Drawing.Color backColor, ColorLabelStates colorLabels, AnimationStates animation)
  294     {
  295         _height = height;
  296         _width = width;
  297         _count = _width * _height;
  298 
  299         _backColor = backColor;
  300 
  301         _animation = animation;
  302         _animationStateStack = new System.Collections.Stack();
  303         _animationState = false;
  304 
  305         _bitmap = CreateNewBitmap(_height, _width, _backColor);
  306 
  307         _colorLabels = colorLabels;
  308         if (_colorLabels == ColorLabelStates.Enabled)
  309         {
  310             _bitmapWithColorLabels = CreateNewBitmap(_height, _width, _backColor);
  311         }
  312 
  313         ResizeTypes(_height, _width);
  314         ResizeLabels(_height, _width);
  315         ResizeColors(_height, _width);
  316 
  317         InitializeBackground();
  318     }
  319 
  320 
  321     //// COPY CONSTRUCTOR //
  322     public Image(Image image)
  323     {
  324         _height = image._height;
  325         _width = image._width;
  326         _count = _width * _height;
  327         _backColor = image._backColor;
  328 
  329         _bitmap = (System.Drawing.Bitmap)image._bitmap.Clone();
  330         _colorLabels = image._colorLabels;
  331         if (_colorLabels == ColorLabelStates.Enabled)
  332         {
  333             _bitmapWithColorLabels = (System.Drawing.Bitmap)image._bitmapWithColorLabels.Clone();
  334         }
  335 
  336         _pixelColorArray = (System.Drawing.Color[])image._pixelColorArray.Clone();
  337         PixelColorProperty = new PixelIndexedProperty<System.Drawing.Color>(_pixelColorArray, this);
  338 
  339         _pixelTypeArray = (PixelTypes[])image._pixelTypeArray.Clone();
  340         PixelTypeProperty = new PixelIndexedProperty<PixelTypes>(_pixelTypeArray, this);
  341 
  342         _pixelLabelArray = (int[])image._pixelLabelArray.Clone();
  343         PixelLabelProperty = new PixelIndexedProperty<int>(_pixelLabelArray, this);
  344 
  345         _animation = image._animation;
  346         _animationState = image._animationState;
  347         _animationStateStack = (System.Collections.Stack)image._animationStateStack.Clone();
  348     }
  349 
  350 
  351     object ICloneable.Clone()
  352     {
  353         //// Clone by invoking copy contructor //
  354         return new Image(this);
  355     }
  356 
  357 
  358     //////////////////////////////////////////////////////////////////////////////////////////////
  359     //// PUBLIC PROPERTIES
  360     //////////////////////////////////////////////////////////////////////////////////////////////
  361 
  362     //// ReturnSystem.Drawing.bitmap object (for rendering on screen) //
  363     public System.Drawing.Bitmap Bitmap(bool withColorLabels)
  364     {
  365         if (withColorLabels)
  366         {
  367             return _bitmapWithColorLabels;
  368         }
  369 
  370         return _bitmap;
  371     }
  372 
  373     //// Get/set color for specified pixel //
  374     public PixelIndexedProperty<System.Drawing.Color> PixelColor
  375     {
  376         get
  377         {
  378             return PixelColorProperty;
  379         }
  380         set
  381         {
  382             PixelColorProperty = value;
  383         }
  384     }
  385 
  386 
  387     //// Get/set type for specified pixel //
  388     public PixelIndexedProperty<PixelTypes> PixelType
  389     {
  390         get { return PixelTypeProperty; }
  391 
  392         set
  393         {
  394 
  395             PixelTypeProperty = value;
  396         }
  397     }
  398 
  399 
  400     //// Get/set label for specified pixel //
  401     public PixelIndexedProperty<int> PixelLabel
  402     {
  403         get { return PixelLabelProperty; }
  404 
  405         set
  406         {
  407 
  408             PixelLabelProperty = value;
  409         }
  410     }
  411 
  412 
  413     //// Get height of image //
  414     public int Height
  415     {
  416         get { return _height; }
  417     }
  418 
  419 
  420     //// Get width of image //
  421     public int Width
  422     {
  423         get { return _width; }
  424     }
  425 
  426     public AnimationStates AnimationState
  427     {
  428         get { return _animation; }
  429 
  430         set { _animation = value; }
  431     }
  432 
  433 
  434     //////////////////////////////////////////////////////////////////////////////////////////////
  435     //// IMAGE RESIZE/CLEAR METHODS
  436     //////////////////////////////////////////////////////////////////////////////////////////////
  437 
  438     public void ResizeColors(int height, int width)
  439     {
  440         AnimationPushState();
  441         AnimationOff();
  442         UpdateStatus("Initializing image");
  443 
  444         _pixelColorArray = new System.Drawing.Color[height * width];
  445         PixelColorProperty = new PixelIndexedProperty<System.Drawing.Color>(_pixelColorArray, this);
  446 
  447         foreach (Pixel pixel in this)
  448         {
  449             UpdateProgress(pixel);
  450             _pixelColorArray[pixel.ArrayIndex(_width)] = _backColor;
  451 
  452             if (CancelOperation)
  453             {
  454                 goto CancelOperation;
  455             }
  456         }
  457     CancelOperation:
  458 
  459         AnimationPopState();
  460         UpdateProgress(0, 0, 0);
  461 
  462     }
  463 
  464 
  465     public void ResizeTypes(int height, int width)
  466     {
  467         _pixelTypeArray = new Image.PixelTypes[(height * width)];
  468         PixelTypeProperty = new PixelIndexedProperty<PixelTypes>(_pixelTypeArray, this);
  469     }
  470 
  471 
  472     public void ResizeLabels(int height, int width)
  473     {
  474         _pixelLabelArray = new int[(height * width)];
  475         PixelLabelProperty = new PixelIndexedProperty<int>(_pixelLabelArray, this);
  476     }
  477 
  478 
  479     public void ClearColors()
  480     {
  481         ResizeColors(_height, _width);
  482 
  483         AnimationRaiseImageChanged();
  484     }
  485 
  486 
  487     public void ClearTypes()
  488     {
  489         ResizeTypes(_height, _width);
  490 
  491         AnimationRaiseImageChanged();
  492     }
  493 
  494 
  495     public void ClearLabels()
  496     {
  497         ResizeLabels(_height, _width);
  498 
  499         if (_colorLabels == ColorLabelStates.Enabled)
  500         {
  501             //// No labels means no color labels either...
  502             _bitmapWithColorLabels = (System.Drawing.Bitmap)(_bitmap.Clone());
  503         }
  504 
  505         AnimationRaiseImageChanged();
  506     }
  507 
  508 
  509     public void ClearBoundaryTypes()
  510     {
  511         UpdateStatus("Changing boundary pixels to foreground pixels");
  512 
  513         AnimationPushState();
  514         AnimationOff();
  515 
  516         ImageTransform(PixelTransformClearBoundaryType, null);
  517 
  518         AnimationPopState();
  519 
  520     }
  521 
  522 
  523     //// Clear entire image //
  524     public void Clear()
  525     {
  526         _bitmap = CreateNewBitmap(_height, _width, _backColor);
  527 
  528         ClearColors();
  529         ClearTypes();
  530         ClearLabels();
  531     }
  532 
  533 
  534     //// Remove labels from all pixels that are not on the boundary of a component
  535     //// ... for cleaning up connected components after marking boundaries //
  536     public void ClearNonBoundaryLabels()
  537     {
  538         UpdateStatus("Removing labels from non-boundary pixels");
  539         AnimationPushState();
  540         AnimationOff();
  541 
  542         foreach (Pixel pixel in this)
  543         {
  544 
  545             UpdateProgress(pixel);
  546 
  547             if (IsForeground(pixel))
  548             {
  549 
  550                 if (!IsBoundary(pixel))
  551                 {
  552 
  553                     PixelLabel[pixel] = 0;
  554                 }
  555             }
  556 
  557             if (CancelOperation)
  558             {
  559                 goto CancelOperation;
  560             }
  561         }
  562     CancelOperation:
  563 
  564         AnimationPopState();
  565         UpdateProgress(0, 0, 0);
  566     }
  567 
  568     //// Resize this image //
  569     public void Resize(int height, int width)
  570     {
  571         FromBitmap(_bitmap, height, width);
  572     }
  573 
  574 
  575     //////////////////////////////////////////////////////////////////////////////////////////////
  576     //// IMAGE LOADING METHODS
  577     //////////////////////////////////////////////////////////////////////////////////////////////
  578 
  579     private System.Drawing.Bitmap CreateNewBitmap(int height, int width, System.Drawing.Color backColor)
  580     {
  581         System.Drawing.Bitmap result;
  582         System.Drawing.Graphics graphics;
  583         System.Drawing.Brush clearBrush;
  584 
  585         result = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  586 
  587         clearBrush = new System.Drawing.SolidBrush(_backColor);
  588         graphics = Graphics.Fro_image(result);
  589         graphics.FillRectangle(clearBrush, 0, 0, width, height);
  590         graphics.Dispose();
  591         clearBrush.Dispose();
  592 
  593         return result;
  594     }
  595 
  596 
  597     //// Initialize this image using the specified bitmap object //
  598     public void FromBitmap(System.Drawing.Bitmap bitmap, int height, int width)
  599     {
  600         System.Drawing.Bitmap newBitmap;
  601         System.Drawing.Graphics graphics;
  602 
  603         int screenHeight;
  604         int screenWidth;
  605 
  606         int newWidth;
  607         int newHeight;
  608         float maxPixelSize;
  609 
  610         Rectangle srcRect;
  611         Rectangle destRect;
  612 
  613         _animationLastScannedPixel = null;
  614 
  615         screenHeight = Screen.PrimaryScreen.WorkingArea.Height;
  616         screenWidth = Screen.PrimaryScreen.WorkingArea.Width;
  617 
  618         //// Preserve aspect ratio
  619         newWidth = Math.Min(width, screenWidth);
  620         if (newWidth < width)
  621         {
  622             height = Convert.ToInt32(height * (Convert.ToSingle(newWidth) / width));
  623         }
  624         width = newWidth;
  625 
  626         //// Preserve aspect ratio
  627         newHeight = Math.Min(height, screenHeight);
  628         if (newHeight < height)
  629         {
  630             width = Convert.ToInt32(width * (Convert.ToSingle(newHeight) / height));
  631         }
  632         height = newHeight;
  633 
  634         //// Size of virtual pixel (in screen pixels) //
  635         maxPixelSize = Math.Min(Convert.ToSingle(screenHeight) / height, Convert.ToSingle(screenWidth) / width);
  636 
  637         newBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  638 
  639         srcRect = new Rectangle(0, 0, bitmap.Size.Width, bitmap.Size.Height);
  640         destRect = new Rectangle(0, 0, width, height);
  641 
  642         graphics = Graphics.Fro_image(newBitmap);
  643 
  644         if (maxPixelSize > 1)
  645         {
  646             //// Virtual pixels are visible, copy bitmap exactly (no interpolation) //
  647             graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
  648             graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
  649             graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
  650             graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
  651             graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
  652         }
  653 
  654         graphics.DrawImage(bitmap, destRect, srcRect, GraphicsUnit.Pixel);
  655 
  656         graphics.Dispose();
  657 
  658         _bitmap = newBitmap;
  659         if (_colorLabels == ColorLabelStates.Enabled)
  660         {
  661             _bitmapWithColorLabels = (System.Drawing.Bitmap)newBitmap.Clone();
  662         }
  663 
  664         lastModified = (lastModified + 1) % 2000000000;
  665 
  666         _height = height;
  667         _width = width;
  668         _count = _width * _height;
  669 
  670         ResizeTypes(_height, _width);
  671         ResizeLabels(_height, _width);
  672         ResizeColors(_height, _width);
  673 
  674         ClearLabels();
  675         ClearTypes();
  676         InitializeBackground();
  677     }
  678 
  679 
  680     public void FromFile(string fileName)
  681     {
  682         System.Drawing.Bitmap newBitmap;
  683 
  684         newBitmap = new Bitmap(fileName);
  685 
  686         FromBitmap(newBitmap, newBitmap.Height, newBitmap.Width);
  687     }
  688 
  689 
  690     public void FromStream(Stream stream)
  691     {
  692         System.Drawing.Bitmap newBitmap;
  693 
  694         newBitmap = new Bitmap(stream);
  695 
  696         FromBitmap(newBitmap, newBitmap.Height, newBitmap.Width);
  697     }
  698 
  699 
  700     public void ToFile(string fileName)
  701     {
  702         _bitmap.Save(fileName);
  703     }
  704 
  705 
  706     //// Separate background pixels from foreground pixels //
  707     private void InitializeBackground()
  708     {
  709 
  710         AnimationPushState();
  711         AnimationOff();
  712         UpdateStatus("Identifying background pixels");
  713 
  714         foreach (Pixel pixel in this)
  715         {
  716 
  717             UpdateProgress(pixel);
  718 
  719             _pixelColorArray[pixel.ArrayIndex(_width)] = _bitmap.GetPixel(pixel.j, pixel.i);
  720 
  721             if (Color.ColorsAreEqual(PixelColor[pixel], _backColor))
  722             {
  723                 PixelType[pixel] = PixelTypes.Background;
  724             }
  725             else
  726             {
  727                 PixelType[pixel] = PixelTypes.Foreground;
  728             }
  729 
  730             if (CancelOperation)
  731             {
  732                 goto CancelOperation;
  733             }
  734         }
  735     CancelOperation:
  736 
  737         AnimationPopState();
  738         UpdateProgress(0, 0, 0);
  739     }
  740 
  741 
  742     //// Assign color labels to every pixel in image //
  743     public void AssignColorLabels()
  744     {
  745         AnimationPushState();
  746         AnimationOff();
  747         UpdateStatus("Assigning color labels");
  748 
  749         if (_colorLabels == ColorLabelStates.Enabled)
  750         {
  751 
  752             foreach (Pixel pixel in this)
  753             {
  754 
  755                 UpdateProgress(pixel);
  756 
  757                 _bitmapWithColorLabels.SetPixel(pixel.j, pixel.i, Color.ColorFromNumber(PixelLabel[pixel], _backColor));
  758 
  759                 if (CancelOperation)
  760                 {
  761                     goto CancelOperation;
  762                 }
  763             }
  764         }
  765     CancelOperation:
  766 
  767         AnimationPopState();
  768         UpdateProgress(0, 0, 0);
  769     }
  770 
  771 
  772     //////////////////////////////////////////////////////////////////////////////////////////////
  773     //// INDIVIDUAL PIXEL METHODS
  774     //////////////////////////////////////////////////////////////////////////////////////////////
  775 
  776     public bool IsBackground(Pixel pixel)
  777     {
  778         if (PixelType[pixel] == PixelTypes.Background)
  779         {
  780             return true;
  781         }
  782 
  783         return false;
  784     }
  785 
  786 
  787     //// For performance, rewrite negated IsBackground()
  788     public bool IsForeground(Pixel pixel)
  789     {
  790         if (PixelType[pixel] == PixelTypes.Background)
  791         {
  792             return false;
  793         }
  794 
  795         return true;
  796     }
  797 
  798 
  799     public bool IsBoundary(Pixel pixel)
  800     {
  801         if (PixelType[pixel] == PixelTypes.Boundary)
  802         {
  803             return true;
  804         }
  805 
  806         return false;
  807     }
  808 
  809 
  810     public bool IsLabeled(Pixel pixel)
  811     {
  812         if (PixelLabel[pixel] != 0)
  813         {
  814             return true;
  815         }
  816 
  817         return false;
  818     }
  819 
  820     //// For performance, rewrite negated IsLabeled() //
  821     public bool IsUlabeled(Pixel pixel)
  822     {
  823         if (PixelLabel[pixel] != 0)
  824         {
  825             return false;
  826         }
  827 
  828         return true;
  829     }
  830 
  831     public bool IsMarked(Pixel pixel)
  832     {
  833         if (PixelLabel[pixel] == PIXEL_LABEL_MARK)
  834         {
  835             return true;
  836         }
  837 
  838         return false;
  839     }
  840 
  841     //// For performance, rewrite negated IsMarked() //
  842     public bool IsUnmarked(Pixel pixel)
  843     {
  844         if (PixelLabel[pixel] == PIXEL_LABEL_MARK)
  845         {
  846             return false;
  847         }
  848 
  849         return true;
  850     }
  851 
  852     public string GetDisplayText(Pixel pixel)
  853     {
  854         if (PixelLabel[pixel] == 0)
  855         {
  856             return "";
  857         }
  858 
  859         if (PixelLabel[pixel] == PIXEL_LABEL_MARK)
  860         {
  861             return "M";
  862         }
  863 
  864         return PixelLabel[pixel].ToString();
  865     }
  866 
  867 
  868     //// Set specified pixel color and type //
  869     public void SetPixel(Pixel pixel, System.Drawing.Color color, PixelTypes nPixelType)
  870     {
  871         PixelType[pixel] = nPixelType;
  872 
  873         if (nPixelType == PixelTypes.Background)
  874         {
  875             PixelLabel[pixel] = 0;
  876             PixelColor[pixel] = _backColor;
  877         }
  878         else
  879         {
  880             PixelColor[pixel] = color;
  881 
  882             if (_colorLabels == ColorLabelStates.Enabled)
  883             {
  884                 _bitmapWithColorLabels.SetPixel(pixel.j, pixel.i, color);
  885             }
  886         }
  887     }
  888 
  889     //// OVERLOADED: Set specified pixel color, type, and label //
  890     public void SetPixel(Pixel pixel, System.Drawing.Color color, PixelTypes nPixelType, int nPixelLabel)
  891     {
  892         PixelLabel[pixel] = nPixelLabel;
  893         SetPixel(pixel, color, nPixelType);
  894     }
  895 
  896 
  897     public void MarkPixel(Pixel pixel)
  898     {
  899         PixelLabel[pixel] = PIXEL_LABEL_MARK;
  900     }
  901 
  902 
  903     public void RemovePixel(Pixel pixel)
  904     {
  905         SetPixel(pixel, _backColor, PixelTypes.Background);
  906     }
  907 
  908 
  909     //// Return a new color object that will contrast with the specified pixel
  910     //// ... for displaying label text //
  911     public System.Drawing.Color FilterContrast(Pixel pixel)
  912     {
  913 
  914         if (PixelColor[pixel].GetBrightness() >= 0.65)
  915         {
  916 
  917             return System.Drawing.Color.Black;
  918         }
  919         else
  920         {
  921 
  922             return System.Drawing.Color.White;
  923         }
  924     }
  925 
  926 
  927     //// Get the 3x3 image around specified pixel (oSourceCenter) //
  928     public Image GetImage3x3(Pixel oSourceCenter)
  929     {
  930         Pixel oSourceNeighbor;
  931         Pixel oTargetNeighbor;
  932         int index;
  933         Image result;
  934 
  935         //// For performance, allocate and re-use a blank 3x3 image //
  936         if (static_GetImage3x3_image3x3 == null)
  937         {
  938             static_GetImage3x3_image3x3 = new Image(3, 3, _backColor, ColorLabelStates.Disabled, AnimationStates.Disabled);
  939             static_GetImage3x3_oTargetCenter = new Pixel(1, 1);
  940         }
  941 
  942         //// Copy constructor is faster than New()
  943         result = new Image(static_GetImage3x3_image3x3);
  944 
  945         //// Copy center pixel //
  946         if (IsInBounds(oSourceCenter))
  947         {
  948             result.PixelType[static_GetImage3x3_oTargetCenter] = PixelType[oSourceCenter];
  949             result.PixelLabel[static_GetImage3x3_oTargetCenter] = PixelLabel[oSourceCenter];
  950         }
  951 
  952         //// Copy all neighbor pixels //
  953         for (index = 0; index <= 7; index++)
  954         {
  955             oSourceNeighbor = oSourceCenter.Neighbor(index);
  956             oTargetNeighbor = static_GetImage3x3_oTargetCenter.Neighbor(index);
  957 
  958             //// Don't copy out-of-bounds pixels (treat as blank) //
  959             if (IsInBounds(oSourceNeighbor))
  960             {
  961 
  962                 result.PixelType[oTargetNeighbor] = PixelType[oSourceNeighbor];
  963                 result.PixelLabel[oTargetNeighbor] = PixelLabel[oSourceNeighbor];
  964             }
  965         }
  966 
  967         return result;
  968     }
  969     static Image static_GetImage3x3_image3x3;
  970     static Pixel static_GetImage3x3_oTargetCenter;
  971 
  972 
  973     //////////////////////////////////////////////////////////////////////////////////////////////
  974     //// GROUP PIXEL METHODS
  975     //////////////////////////////////////////////////////////////////////////////////////////////
  976 
  977     public void RemoveCollectedPixels(ICollection oCollection)
  978     {
  979         int progressMax;
  980         int progressCount;
  981 
  982         progressMax = oCollection.Count;
  983         progressCount = 0;
  984 
  985         UpdateStatus("Removing collected pixels");
  986 
  987         foreach (Pixel pixel in oCollection)
  988         {
  989 
  990             progressCount = progressCount + 1;
  991             //UpdateProgress(0, progressMax, progressCount, False)
  992 
  993             if (PixelLabel[pixel] == PIXEL_LABEL_MARK)
  994             {
  995                 RemovePixel(pixel);
  996             }
  997 
  998             if (CancelOperation)
  999             {
 1000                 goto CancelOperation;
 1001             }
 1002         }
 1003     CancelOperation:
 1004         return;
 1005     }
 1006 
 1007 
 1008     public void SetCollectedPixels(ICollection oCollection)
 1009     {
 1010         UpdateStatus("Setting collected pixels");
 1011         _progressMax = oCollection.Count;
 1012         _progressCount = 0;
 1013 
 1014         foreach (Pixel pixel in oCollection)
 1015         {
 1016 
 1017             _progressCount = _progressCount + 1;
 1018             //UpdateProgress(0, _progressMax, _progressCount, False)
 1019 
 1020             SetPixel(pixel, System.Drawing.Color.Gray, PixelTypes.Foreground);
 1021 
 1022             if (CancelOperation)
 1023             {
 1024                 goto CancelOperation;
 1025             }
 1026         }
 1027     CancelOperation:
 1028         return;
 1029     }
 1030 
 1031 
 1032     public void MarkCollectedPixels(ICollection oCollection)
 1033     {
 1034         UpdateStatus("Marking collected pixels");
 1035         _progressMax = oCollection.Count;
 1036         _progressCount = 0;
 1037 
 1038         foreach (Pixel pixel in oCollection)
 1039         {
 1040 
 1041             _progressCount = _progressCount + 1;
 1042             //UpdateProgress(0, _progressMax, _progressCount, False)
 1043 
 1044             PixelLabel[pixel] = PIXEL_LABEL_MARK;
 1045 
 1046             if (CancelOperation)
 1047             {
 1048                 goto CancelOperation;
 1049             }
 1050         }
 1051     CancelOperation:
 1052         return;
 1053     }
 1054 
 1055 
 1056     //// Build a tree of all boundary pixels //
 1057     public PixelCollectionTree GetBoundaryPixelCollectionTree()
 1058     {
 1059         PixelCollectionTree result;
 1060 
 1061         UpdateStatus("Collecting boundary pixels");
 1062         AnimationPushState();
 1063         AnimationOff();
 1064 
 1065         result = new PixelCollectionTree();
 1066 
 1067         foreach (Pixel pixel in this)
 1068         {
 1069 
 1070             UpdateProgress(pixel);
 1071 
 1072             if (IsBoundary(pixel))
 1073             {
 1074                 result.Add(pixel);
 1075             }
 1076 
 1077             if (CancelOperation)
 1078             {
 1079                 goto CancelOperation;
 1080             }
 1081         }
 1082     CancelOperation:
 1083 
 1084         AnimationPopState();
 1085         UpdateProgress(0, 0, 0);
 1086 
 1087         return result;
 1088     }
 1089 
 1090 
 1091     //////////////////////////////////////////////////////////////////////////////////////////////
 1092     //// PIXEL POSITION METHODS
 1093     //////////////////////////////////////////////////////////////////////////////////////////////
 1094 
 1095     //// Determine if specified pixel position is outside of image boundary //
 1096     public bool IsOutOfBounds(Pixel pixel)
 1097     {
 1098         int i;
 1099         int j;
 1100 
 1101         i = pixel.i;
 1102 
 1103         if (i < 0)
 1104         {
 1105             return true;
 1106         }
 1107 
 1108         if (i >= _height)
 1109         {
 1110             return true;
 1111         }
 1112 
 1113         j = pixel.j;
 1114 
 1115         if (j < 0)
 1116         {
 1117             return true;
 1118         }
 1119 
 1120         if (j >= _width)
 1121         {
 1122             return true;
 1123         }
 1124 
 1125         return false;
 1126     }
 1127 
 1128 
 1129     //// For performance, rewrite negated logic for IsOutOfBounds //
 1130     public bool IsInBounds(Pixel pixel)
 1131     {
 1132         int i;
 1133         int j;
 1134 
 1135         i = pixel.i;
 1136 
 1137         if (i < 0)
 1138         {
 1139             return false;
 1140         }
 1141 
 1142         if (i >= _height)
 1143         {
 1144             return false;
 1145         }
 1146 
 1147         j = pixel.j;
 1148 
 1149         if (j < 0)
 1150         {
 1151             return false;
 1152         }
 1153 
 1154         if (j >= _width)
 1155         {
 1156             return false;
 1157         }
 1158 
 1159         AnimationRaisePixelScanned(pixel);
 1160 
 1161         return true;
 1162     }
 1163 
 1164 
 1165     //// If specified pixel is outside image boundary, snap it to just inside the closest edge //
 1166     public Pixel SnapPixelToEdge(Pixel pixel)
 1167     {
 1168         if (pixel.i < 0)
 1169         {
 1170             pixel.i = 0;
 1171         }
 1172 
 1173         if (pixel.j < 0)
 1174         {
 1175             pixel.j = 0;
 1176         }
 1177 
 1178         if (pixel.i >= _height)
 1179         {
 1180             pixel.i = _height - 1;
 1181         }
 1182 
 1183         if (pixel.j >= _width)
 1184         {
 1185             pixel.j = _width - 1;
 1186         }
 1187 
 1188         return pixel;
 1189     }
 1190 
 1191 
 1192     public int Count4Neighbors(Pixel pixel)
 1193     {
 1194         return Count4Neighbors(pixel, 4);
 1195     }
 1196 
 1197     public int Count4Neighbors(Pixel pixel, int nReturnIfGreaterThan)
 1198     {
 1199         int result;
 1200 
 1201         result = 0;
 1202 
 1203         foreach (Pixel oNeighbor in pixel.Get4Neighbors())
 1204         {
 1205 
 1206             if (IsInBounds(oNeighbor))
 1207             {
 1208 
 1209                 if (IsForeground(oNeighbor))
 1210                 {
 1211                     result = result + 1;
 1212 
 1213                     if ((result > nReturnIfGreaterThan))
 1214                     {
 1215                         return result;
 1216                     }
 1217                 }
 1218 
 1219             }
 1220         }
 1221 
 1222         return result;
 1223     }
 1224 
 1225 
 1226     public int Count8Neighbors(Pixel pixel)
 1227     {
 1228         return Count8Neighbors(pixel, 8);
 1229     }
 1230 
 1231     public int Count8Neighbors(Pixel pixel, int nReturnIfGreaterThan)
 1232     {
 1233         int result;
 1234 
 1235         result = 0;
 1236 
 1237         foreach (Pixel oNeighbor in pixel.Get8Neighbors())
 1238         {
 1239             if (IsInBounds(oNeighbor))
 1240             {
 1241 
 1242                 if (IsForeground(oNeighbor))
 1243                 {
 1244                     result = result + 1;
 1245 
 1246                     if (result > nReturnIfGreaterThan)
 1247                     {
 1248                         return result;
 1249                     }
 1250                 }
 1251 
 1252             }
 1253         }
 1254 
 1255         return result;
 1256     }
 1257 
 1258 
 1259     //// Search through a range of neighbors (by index) and return the count of foreground pixels //
 1260     public int CountRangeNeighbors(Pixel pixel, int nMin, int nMax)
 1261     {
 1262         int n;
 1263         Pixel oNeighbor;
 1264         int result;
 1265 
 1266         result = 0;
 1267 
 1268         for (n = nMin; n <= nMax; n++)
 1269         {
 1270             oNeighbor = pixel.Neighbor(n % 8);
 1271 
 1272             if (IsInBounds(oNeighbor))
 1273             {
 1274                 if (IsForeground(oNeighbor))
 1275                 {
 1276                     result = result + 1;
 1277                 }
 1278             }
 1279         }
 1280 
 1281         return result;
 1282     }
 1283 
 1284 
 1285     //////////////////////////////////////////////////////////////////////////////////////////////
 1286     //// IMAGE TRANSFORM METHODS
 1287     //////////////////////////////////////////////////////////////////////////////////////////////
 1288 
 1289     public void ImageTransform(PixelTransformDelegate pixelTransformDelegate, PixelTransformOptions pixelTransformOptions)
 1290     {
 1291         foreach (Pixel pixel in this)
 1292         {
 1293 
 1294             UpdateProgress(pixel);
 1295 
 1296             pixelTransformDelegate(this, pixel, pixelTransformOptions);
 1297 
 1298             if (CancelOperation)
 1299             {
 1300                 goto CancelOperation;
 1301             }
 1302         }
 1303 
 1304         AnimationRaiseImageChanged();
 1305     CancelOperation:
 1306 
 1307         UpdateProgress(0, 0, 0);
 1308     }
 1309 
 1310 
 1311     public static void PixelTransformRed(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1312     {
 1313         if (image.IsForeground(pixel))
 1314         {
 1315             image.PixelColor[pixel] = System.Drawing.ColorTranslator.FromWin32(Information.RGB(image.PixelColor[pixel].R, 0, 0));
 1316         }
 1317     }
 1318 
 1319 
 1320     public static void PixelTransformGreen(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1321     {
 1322         if (image.IsForeground(pixel))
 1323         {
 1324             image.PixelColor[pixel] = System.Drawing.ColorTranslator.FromWin32(Information.RGB(0, image.PixelColor[pixel].G, 0));
 1325         }
 1326     }
 1327 
 1328 
 1329     public static void PixelTransformBlue(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1330     {
 1331         if (image.IsForeground(pixel))
 1332         {
 1333             image.PixelColor[pixel] = System.Drawing.ColorTranslator.FromWin32(Information.RGB(0, 0, image.PixelColor[pixel].B));
 1334         }
 1335     }
 1336 
 1337 
 1338     public static void PixelTransformMonochrome(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1339     {
 1340         int brightness;
 1341 
 1342         if (image.IsForeground(pixel))
 1343         {
 1344             brightness = Convert.ToInt32(image.PixelColor[pixel].GetBrightness() * 255);
 1345             image.PixelColor[pixel] = System.Drawing.ColorTranslator.FromWin32(Information.RGB(brightness, brightness, brightness));
 1346         }
 1347     }
 1348 
 1349     public static byte Not(byte b)
 1350     {
 1351         return Convert.ToByte((~b) & 255);
 1352     }
 1353 
 1354     public static void PixelTransformNegative(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1355     {
 1356         image.PixelColor[pixel] = System.Drawing.ColorTranslator.FromWin32(Information.RGB(Not(image.PixelColor[pixel].R), Not(image.PixelColor[pixel].G), Not(image.PixelColor[pixel].B)));
 1357 
 1358         if (Color.ColorsAreEqual(image.PixelColor[pixel], image._backColor))
 1359         {
 1360 
 1361             image.PixelType[pixel] = PixelTypes.Background;
 1362         }
 1363         else
 1364         {
 1365             image.PixelType[pixel] = PixelTypes.Foreground;
 1366         }
 1367     }
 1368 
 1369 
 1370     public static void PixelTransformThreshold(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1371     {
 1372         int brightness;
 1373         bool bKeepPixel;
 1374         TransformOptionsThreshold thresholdOptions;
 1375 
 1376         //// Don't process background pixels (they're already removed, silly) //
 1377         if (image.IsBackground(pixel))
 1378         {
 1379             return;
 1380         }
 1381 
 1382         //// Get threshold options //
 1383         thresholdOptions = (TransformOptionsThreshold)pixelTransformOptions;
 1384 
 1385         //// Get brightness of current pixel //
 1386         brightness = Convert.ToInt32(image.PixelColor[pixel].GetBrightness() * 255);
 1387 
 1388         //// Check if brightness falls within threshold range //
 1389         if (thresholdOptions.RemovePixelsAbove > thresholdOptions.RemovePixelsBelow)
 1390         {
 1391             if (brightness > thresholdOptions.RemovePixelsAbove | brightness < thresholdOptions.RemovePixelsBelow)
 1392             {
 1393                 bKeepPixel = false;
 1394             }
 1395             else
 1396             {
 1397                 bKeepPixel = true;
 1398             }
 1399         }
 1400         else
 1401         {
 1402             if (brightness >= thresholdOptions.RemovePixelsAbove & brightness <= thresholdOptions.RemovePixelsBelow)
 1403             {
 1404                 bKeepPixel = false;
 1405             }
 1406             else
 1407             {
 1408                 bKeepPixel = true;
 1409             }
 1410         }
 1411 
 1412         //// Check if pixel should be removed //
 1413         if (bKeepPixel == false)
 1414         {
 1415 
 1416             //// Pixel selected for removal, remove it //
 1417             image.RemovePixel(pixel);
 1418         }
 1419     }
 1420 
 1421 
 1422     //// Convert boundary pixels to foreground pixels
 1423     //// (... clear existing boundaries before boundary search)
 1424     public static void PixelTransformClearBoundaryType(Image image, Pixel pixel, PixelTransformOptions pixelTransformOptions)
 1425     {
 1426         if (image.IsBoundary(pixel))
 1427         {
 1428             image.PixelType[pixel] = PixelTypes.Foreground;
 1429         }
 1430     }
 1431 
 1432 
 1433     //////////////////////////////////////////////////////////////////////////////////////////////
 1434     //// IMAGE PROCESSING METHODS
 1435     //////////////////////////////////////////////////////////////////////////////////////////////
 1436 
 1437     public Histogram GetHistogram()
 1438     {
 1439         return new Histogram(this);
 1440     }
 1441 
 1442 
 1443     //// Assign unique labels to each connected component in image //
 1444     public int LabelComponents()
 1445     {
 1446         int nComponentNumber;
 1447 
 1448         ClearLabels();
 1449         ClearBoundaryTypes();
 1450 
 1451         nComponentNumber = 0;
 1452 
 1453         _progressMax = _count + (_count / 2);
 1454         _progressCount = 0;
 1455 
 1456         UpdateStatus("Identifying connected components");
 1457 
 1458         foreach (Pixel pixel in this)
 1459         {
 1460 
 1461             _progressCount = _progressCount + 1;
 1462             UpdateProgress(0, _progressMax, _progressCount);
 1463 
 1464             if (IsForeground(pixel))
 1465             {
 1466 
 1467                 if (IsUlabeled(pixel))
 1468                 {
 1469 
 1470                     nComponentNumber = nComponentNumber + 1;
 1471 
 1472                     UpdateStatus("Identifying connected components (" + nComponentNumber + " so far)");
 1473 
 1474                     LabelConnectedPixels(pixel, nComponentNumber);
 1475 
 1476                 }
 1477             }
 1478 
 1479             if (CancelOperation)
 1480             {
 1481                 goto CancelOperation;
 1482             }
 1483         }
 1484     CancelOperation:
 1485 
 1486         return nComponentNumber;
 1487     }
 1488 
 1489 
 1490     //// Given a pixel, assign specified label to all pixels that are connected to it //
 1491     public void LabelConnectedPixels(Pixel pixel, int nComponentNumber)
 1492     {
 1493         PixelCollectionList pixelCollection;
 1494         PixelCollectionList oNeighborCollection;
 1495 
 1496         //// Recursive version of this routine causes stack overflow in .NET //
 1497         //// Perform operation iteratively using pixel collections //
 1498 
 1499         if (IsOutOfBounds(pixel))
 1500         {
 1501             return;
 1502         }
 1503 
 1504         if (IsBackground(pixel))
 1505         {
 1506             return;
 1507         }
 1508 
 1509         pixelCollection = new PixelCollectionList();
 1510         oNeighborCollection = pixelCollection;
 1511 
 1512         PixelLabel[pixel] = nComponentNumber;
 1513         pixelCollection.Add(pixel);
 1514 
 1515         while (pixelCollection.Count > 0)
 1516         {
 1517             pixelCollection = oNeighborCollection;
 1518             oNeighborCollection = new PixelCollectionList();
 1519 
 1520             foreach (Pixel pixelItem in pixelCollection)
 1521             {
 1522 
 1523                 foreach (Pixel oNeighbor in pixelItem.Get8Neighbors())
 1524                 {
 1525 
 1526                     if (IsInBounds(oNeighbor))
 1527                     {
 1528 
 1529                         if (IsForeground(oNeighbor))
 1530                         {
 1531 
 1532                             if (IsUlabeled(oNeighbor))
 1533                             {
 1534 
 1535                                 PixelLabel[oNeighbor] = nComponentNumber;
 1536                                 oNeighborCollection.Add(oNeighbor);
 1537 
 1538                                 _progressCount = _progressCount + 1;
 1539                                 UpdateProgress(0, _progressMax, _progressCount);
 1540                             }
 1541                         }
 1542                     }
 1543                 }
 1544 
 1545                 if (CancelOperation)
 1546                 {
 1547                     goto CancelOperation;
 1548                 }
 1549             }
 1550         }
 1551     CancelOperation:
 1552         return;
 1553     }
 1554 
 1555 
 1556     //// Label connected components, then find (and mark) the boundary pixels for all components //
 1557     public void IdentifyBoundaries(bool bAnimation)
 1558     {
 1559         AnimationPushState();
 1560         AnimationOff();
 1561 
 1562         ClearBoundaryTypes();
 1563         LabelComponents();
 1564 
 1565         UpdateStatus("Identifying component boundaries");
 1566 
 1567         _progressMax = _count + (_count / 4);
 1568         _progressCount = 0;
 1569 
 1570         if (bAnimation)
 1571         {
 1572             AnimationOn();
 1573             AnimationRaiseImageChanged();
 1574         }
 1575 
 1576 
 1577         foreach (Pixel pixel in this)
 1578         {
 1579 
 1580             _progressCount = _progressCount + 1;
 1581             UpdateProgress(0, _progressMax, _progressCount);
 1582 
 1583             if (IsForeground(pixel))
 1584             {
 1585 
 1586                 if (!IsBoundary(pixel))
 1587                 {
 1588 
 1589                     if (Count4Neighbors(pixel) < 4)
 1590                     {
 1591                         SetBoundaryPixels(pixel, PixelLabel[pixel]);
 1592                     }
 1593 
 1594                 }
 1595             }
 1596 
 1597             if (CancelOperation)
 1598             {
 1599                 goto CancelOperation;
 1600             }
 1601         }
 1602     CancelOperation:
 1603 
 1604         AnimationPopState();
 1605         UpdateProgress(0, 0, 0);
 1606     }
 1607 
 1608 
 1609     //// Given a pixel, follow the boundary of connected pixels and 
 1610     //// mark them as boundary pixels //
 1611     private void SetBoundaryPixels(Pixel pixel, int nComponentNumber)
 1612     {
 1613         Pixel s;
 1614         Pixel c;
 1615         Pixel n;
 1616         int k;
 1617 
 1618         bool bFinished;
 1619         bool bFoundBoundaryPixel;
 1620 
 1621         bool bFoundStartingPoint;
 1622         Pixel oStartingPoint = null;
 1623         int nPixelCount;
 1624 
 1625         bool bFoundBackground;
 1626 
 1627         c = new Pixel(pixel);
 1628 
 1629         //// Make sure that starting pixel is not surrounded (just in case) //
 1630         if (Count4Neighbors(c) == 4)
 1631         {
 1632             //// Pixel is surrounded, not a boundary pixel!
 1633             System.Diagnostics.Debugger.Break();
 1634             return;
 1635         }
 1636 
 1637         s = new Pixel(pixel);
 1638         k = 0;
 1639 
 1640         //// Start with first 4-neighbor background pixel around c (in clockwise direction)
 1641         bFoundBackground = false;
 1642         while (!bFoundBackground)
 1643         {
 1644             nPixelCount = 0;
 1645 
 1646             n = c.Neighbor(k);
 1647 
 1648             if (IsInBounds(n))
 1649             {
 1650 
 1651                 if (IsBackground(n))
 1652                 {
 1653 
 1654                     bFoundBackground = true;
 1655                 }
 1656             }
 1657             else
 1658             {
 1659 
 1660                 bFoundBackground = true;
 1661             }
 1662 
 1663             if (!bFoundBackground)
 1664             {
 1665                 k = (k + 2) % 8;
 1666             }
 1667 
 1668             nPixelCount = nPixelCount + 1;
 1669 
 1670             if (nPixelCount > 4)
 1671             {
 1672                 System.Diagnostics.Debugger.Break();
 1673                 //// Starting pixel surrounded, not a border pixel!
 1674                 goto CancelOperation;
 1675             }
 1676 
 1677             if (CancelOperation)
 1678             {
 1679                 goto CancelOperation;
 1680             }
 1681         }
 1682 
 1683         bFoundStartingPoint = false;
 1684         bFinished = false;
 1685 
 1686         while (!bFinished)
 1687         {
 1688 
 1689             nPixelCount = 1;
 1690 
 1691             bFoundBoundaryPixel = false;
 1692 
 1693             //// Find next boundary pixel //
 1694             while (!bFoundBoundaryPixel)
 1695             {
 1696                 n = c.Neighbor(k);
 1697 
 1698                 if (IsInBounds(n))
 1699                 {
 1700 
 1701                     if (PixelLabel[n] == nComponentNumber)
 1702                     {
 1703 
 1704                         bFoundBoundaryPixel = true;
 1705 
 1706                         _progressCount = _progressCount + 1;
 1707                         UpdateProgress(0, _progressMax, _progressCount);
 1708 
 1709                         //// Check if this is the first boundary pixel found //
 1710                         if (!bFoundStartingPoint)
 1711                         {
 1712 
 1713                             //// Remember first pixel
 1714                             //// when search reaches this pixel again, it is finished //
 1715                             bFoundStartingPoint = true;
 1716                             oStartingPoint = n;
 1717                         }
 1718 
 1719                         else
 1720                         {
 1721 
 1722                             //// Check if search is finished 
 1723                             if (c.Equals(s))
 1724                             {
 1725 
 1726                                 if (n.Equals(oStartingPoint))
 1727                                 {
 1728 
 1729                                     //// Boundary search complete
 1730                                     bFinished = true;
 1731                                 }
 1732                             }
 1733                         }
 1734 
 1735                         c = n;
 1736 
 1737                         //// Continue clockwise scan from next neighbor (relative to new c)
 1738                         switch (k)
 1739                         {
 1740                             case 0:
 1741                             case 1:
 1742                                 k = 6;
 1743                                 break;
 1744                             case 2:
 1745                             case 3:
 1746                                 k = 0;
 1747                                 break;
 1748                             case 4:
 1749                             case 5:
 1750                                 k = 2;
 1751                                 break;
 1752                             case 6:
 1753                             case 7:
 1754                                 k = 4;
 1755                                 break;
 1756                             default:
 1757                                 System.Diagnostics.Debugger.Break();
 1758                                 break;
 1759                         }
 1760                     }
 1761                     else
 1762                     {
 1763                         //// Handle component composed of only one pixel
 1764                         nPixelCount = nPixelCount + 1;
 1765 
 1766                         //// A component with 1 pixel has no connected boundary pixels...
 1767                         if (nPixelCount > 8)
 1768                         {
 1769                             bFoundBoundaryPixel = true;
 1770                             bFinished = true;
 1771                         }
 1772                     }
 1773                 }
 1774 
 1775                 //// Prepare to scan next neighbor
 1776                 k = (k + 1) % 8;
 1777 
 1778                 if (CancelOperation)
 1779                 {
 1780                     goto CancelOperation;
 1781                 }
 1782 
 1783             }
 1784 
 1785             //// Boundary pixel found, set Pixel Type accordingly //
 1786             PixelType[c] = PixelTypes.Boundary;
 1787 
 1788         }
 1789     CancelOperation:
 1790         return;
 1791     }
 1792 
 1793 
 1794     //// Assign a unique label to each pixel in the image
 1795     //// based on its distance from the background
 1796     public void LabelComponentDistances()
 1797     {
 1798         PixelCollectionList oDistanceCollection;
 1799         PixelCollectionList oNestedCollection;
 1800         int nDistance;
 1801 
 1802         oDistanceCollection = new PixelCollectionList();
 1803 
 1804         AnimationPushState();
 1805         AnimationOff();
 1806 
 1807         ClearLabels();
 1808         ClearBoundaryTypes();
 1809 
 1810         AnimationOn();
 1811         AnimationRaiseImageChanged();
 1812 
 1813         nDistance = 1;
 1814 
 1815         UpdateStatus("Identifying pixels at distance 1");
 1816 
 1817         _progressMax = _count;
 1818 
 1819         //// Assign "1" label to all pixels next to a background pixel //
 1820         foreach (Pixel pixel in this)
 1821         {
 1822 
 1823             UpdateProgress(pixel);
 1824 
 1825             if (IsBackground(pixel))
 1826             {
 1827 
 1828                 foreach (Pixel oNeighbor in pixel.Get4Neighbors())
 1829                 {
 1830 
 1831                     if (IsInBounds(oNeighbor))
 1832                     {
 1833 
 1834                         if (IsForeground(oNeighbor))
 1835                         {
 1836 
 1837                             if (IsUlabeled(oNeighbor))
 1838                             {
 1839 
 1840                                 _progressMax = _progressMax - 1;
 1841 
 1842                                 PixelLabel[oNeighbor] = nDistance;
 1843                                 oDistanceCollection.Add(oNeighbor);
 1844                             }
 1845 
 1846                         }
 1847                     }
 1848                 }
 1849             }
 1850 
 1851             if (CancelOperation)
 1852             {
 1853                 goto CancelOperation;
 1854             }
 1855         }
 1856 
 1857         _progressCount = 0;
 1858 
 1859         oNestedCollection = oDistanceCollection;
 1860 
 1861         //// For each previously labelled pixel,
 1862         //// assign new distance label to its ulabeled neighbors //
 1863         while (oNestedCollection.Count > 0)
 1864         {
 1865 
 1866             oDistanceCollection = oNestedCollection;
 1867             oNestedCollection = new PixelCollectionList();
 1868 
 1869             nDistance = nDistance + 1;
 1870             UpdateStatus("Identifying pixels at distance " + nDistance);
 1871 
 1872             foreach (Pixel oDistancePixel in oDistanceCollection)
 1873             {
 1874 
 1875                 UpdateProgress(0, _progressMax, _progressCount);
 1876 
 1877                 foreach (Pixel oNeighbor in oDistancePixel.Get4Neighbors())
 1878                 {
 1879 
 1880                     if (IsInBounds(oNeighbor))
 1881                     {
 1882 
 1883                         if (IsForeground(oNeighbor))
 1884                         {
 1885 
 1886                             if (IsUlabeled(oNeighbor))
 1887                             {
 1888 
 1889                                 _progressCount = _progressCount + 1;
 1890 
 1891                                 PixelLabel[oNeighbor] = nDistance;
 1892                                 oNestedCollection.Add(oNeighbor);
 1893 
 1894                             }
 1895                         }
 1896                     }
 1897                 }
 1898 
 1899                 if (CancelOperation)
 1900                 {
 1901                     goto CancelOperation;
 1902                 }
 1903             }
 1904         }
 1905     CancelOperation:
 1906 
 1907         AnimationPopState();
 1908         UpdateProgress(0, 0, 0);
 1909     }
 1910 
 1911 
 1912     //// For each background pixel in the image,
 1913     //// if 1 or more of its neighbors is a foreground pixel, 
 1914     //// make it a foreground pixel.
 1915     public void Expand()
 1916     {
 1917         PixelCollectionList oMarkedPixelCollection;
 1918 
 1919         oMarkedPixelCollection = new PixelCollectionList();
 1920 
 1921         ClearLabels();
 1922 
 1923         UpdateStatus("Expanding image");
 1924 
 1925         foreach (Pixel pixel in this)
 1926         {
 1927 
 1928             UpdateProgress(pixel);
 1929 
 1930             if (IsBackground(pixel))
 1931             {
 1932 
 1933                 if (IsUnmarked(pixel))
 1934                 {
 1935 
 1936                     if (Count8Neighbors(pixel, 0) > 0)
 1937                     {
 1938 
 1939                         MarkPixel(pixel);
 1940                         oMarkedPixelCollection.Add(pixel);
 1941 
 1942                     }
 1943                 }
 1944             }
 1945 
 1946             if (CancelOperation)
 1947             {
 1948                 goto CancelOperation;
 1949             }
 1950         }
 1951 
 1952         SetCollectedPixels(oMarkedPixelCollection);
 1953         ClearLabels();
 1954     CancelOperation:
 1955         return;
 1956     }
 1957 
 1958 
 1959     //// For each foreground pixel in the image,
 1960     //// if the pixel is not surrounded (8 neighbors)
 1961     //// make it a background pixel.
 1962     public void Shrink()
 1963     {
 1964         PixelCollectionList oMarkedPixelCollection;
 1965 
 1966         oMarkedPixelCollection = new PixelCollectionList();
 1967 
 1968         ClearLabels();
 1969 
 1970         UpdateStatus("Shrinking image");
 1971 
 1972         foreach (Pixel pixel in this)
 1973         {
 1974 
 1975             UpdateProgress(pixel);
 1976 
 1977             if (IsForeground(pixel))
 1978             {
 1979 
 1980                 if (IsUnmarked(pixel))
 1981                 {
 1982 
 1983                     if (Count8Neighbors(pixel) < 8)
 1984                     {
 1985 
 1986                         MarkPixel(pixel);
 1987                         oMarkedPixelCollection.Add(pixel);
 1988                     }
 1989                 }
 1990             }
 1991 
 1992             if (CancelOperation)
 1993             {
 1994                 goto CancelOperation;
 1995             }
 1996         }
 1997 
 1998         RemoveCollectedPixels(oMarkedPixelCollection);
 1999         ClearLabels();
 2000     CancelOperation:
 2001         return;
 2002     }
 2003 
 2004 
 2005     //// Iteratively remove boundary pixels until
 2006     //// no more pixels can be removed without breaking
 2007     //// a connected component
 2008     public void Thin()
 2009     {
 2010 
 2011         PixelCollectionTree oBoundaryPixels;
 2012 
 2013         PixelCollectionList oThinnedPixels;
 2014 
 2015         Image image3x3;
 2016         Pixel oTargetPixel;
 2017 
 2018         int progressMin;
 2019         int progressCount;
 2020 
 2021         int nComponentCount;
 2022         int nNewComponentCount;
 2023 
 2024         bool bRemovePixel;
 2025 
 2026 
 2027         AnimationPushState();
 2028         AnimationOff();
 2029 
 2030         //// Target pixel for removal is always center of 3x3 image
 2031         oTargetPixel = new Pixel(1, 1);
 2032 
 2033         oThinnedPixels = new PixelCollectionList();
 2034 
 2035         //// First, identify boundaries //
 2036         IdentifyBoundaries(false);
 2037 
 2038         //// For efficiency, create a tree of boundary pixels //
 2039         oBoundaryPixels = GetBoundaryPixelCollectionTree();
 2040 
 2041         AnimationOn();
 2042         AnimationRaiseImageChanged(true);
 2043 
 2044         progressMin = oBoundaryPixels.Count;
 2045 
 2046         //// Do until there are no more boundary pixels to consider //
 2047         while (oBoundaryPixels.Count > 0)
 2048         {
 2049 
 2050             progressCount = oBoundaryPixels.Count;
 2051             UpdateProgress(progressMin, 0, progressCount, true);
 2052             AnimationRaiseImageChanged(true);
 2053 
 2054             UpdateStatus("Thinning corner pixels");
 2055 
 2056             //// Don't animate corner pixel (hack)
 2057             AnimationOff();
 2058 
 2059             foreach (Pixel oBoundaryPixel in oBoundaryPixels)
 2060             {
 2061 
 2062                 bRemovePixel = false;
 2063 
 2064                 if (IsForeground(oBoundaryPixel))
 2065                 {
 2066 
 2067                     if (IsUnmarked(oBoundaryPixel))
 2068                     {
 2069 
 2070                         if (Count8Neighbors(oBoundaryPixel, 3) == 3)
 2071                         {
 2072 
 2073                             //// Top left corner pixel
 2074                             if (CountRangeNeighbors(oBoundaryPixel, 4, 6) == 3)
 2075                             {
 2076                                 bRemovePixel = true;
 2077                             }
 2078 
 2079                             //// Top right corner pixel
 2080                             else if (CountRangeNeighbors(oBoundaryPixel, 6, 8) == 3)
 2081                             {
 2082                                 bRemovePixel = true;
 2083                             }
 2084 
 2085                             //// Bottom left corner pixel
 2086                             else if (CountRangeNeighbors(oBoundaryPixel, 2, 4) == 3)
 2087                             {
 2088                                 bRemovePixel = true;
 2089                             }
 2090 
 2091                             //// Bottom right corner pixel
 2092                             else if (CountRangeNeighbors(oBoundaryPixel, 0, 2) == 3)
 2093                             {
 2094                                 bRemovePixel = true;
 2095 
 2096                             }
 2097                         }
 2098                     }
 2099                 }
 2100 
 2101                 if (bRemovePixel)
 2102                 {
 2103                     AnimationOn();
 2104                     oThinnedPixels.Add(oBoundaryPixel);
 2105                     RemovePixel(oBoundaryPixel);
 2106                     AnimationOff();
 2107                 }
 2108 
 2109                 if (CancelOperation)
 2110                 {
 2111                     goto CancelOperation;
 2112                 }
 2113             }
 2114 
 2115             AnimationOn();
 2116             UpdateStatus("Thinning image");
 2117 
 2118             //// Clear marked pixels collection //
 2119             oThinnedPixels = new PixelCollectionList();
 2120 
 2121             foreach (Pixel oBoundaryPixel in oBoundaryPixels)
 2122             {
 2123 
 2124                 bRemovePixel = true;
 2125 
 2126                 if (Count8Neighbors(oBoundaryPixel, 3) == 2)
 2127                 {
 2128 
 2129                     if (CountRangeNeighbors(oBoundaryPixel, 6, 7) == 2)
 2130                     {
 2131 
 2132                         Pixel oNeighbor;
 2133 
 2134                         oNeighbor = oBoundaryPixel.Neighbor(7);
 2135 
 2136                         if (IsInBounds(oNeighbor))
 2137                         {
 2138 
 2139                             if (CountRangeNeighbors(oNeighbor, 3, 6) == 4)
 2140                             {
 2141                                 if (Count8Neighbors(oNeighbor, 4) == 4)
 2142                                 {
 2143                                     bRemovePixel = false;
 2144                                 }
 2145                             }
 2146                             else if (CountRangeNeighbors(oNeighbor, 3, 5) == 3)
 2147                             {
 2148                                 if (Count8Neighbors(oNeighbor, 3) == 3)
 2149                                 {
 2150                                     bRemovePixel = false;
 2151                                 }
 2152                             }
 2153                         }
 2154 
 2155                     }
 2156                 }
 2157 
 2158                 if (bRemovePixel)
 2159                 {
 2160 
 2161                     bRemovePixel = false;
 2162 
 2163                     if (Count8Neighbors(oBoundaryPixel, 1) > 1)
 2164                     {
 2165                         image3x3 = GetImage3x3(oBoundaryPixel);
 2166 
 2167                         nComponentCount = image3x3.LabelComponents();
 2168                         image3x3.RemovePixel(oTargetPixel);
 2169                         nNewComponentCount = image3x3.LabelComponents();
 2170 
 2171                         if (nNewComponentCount == nComponentCount)
 2172                         {
 2173                             bRemovePixel = true;
 2174                         }
 2175                     }
 2176                 }
 2177 
 2178                 if (bRemovePixel)
 2179                 {
 2180                     oThinnedPixels.Add(oBoundaryPixel);
 2181                     RemovePixel(oBoundaryPixel);
 2182                 }
 2183 
 2184                 if (CancelOperation)
 2185                 {
 2186                     goto CancelOperation;
 2187                 }
 2188 
 2189             }
 2190 
 2191             oBoundaryPixels = new PixelCollectionTree();
 2192 
 2193             //// Determine the new boundary (from thinned pixels) //
 2194             foreach (Pixel oThinnedPixel in oThinnedPixels)
 2195             {
 2196 
 2197                 foreach (Pixel oNeighborItem in oThinnedPixel.Get4Neighbors())
 2198                 {
 2199 
 2200                     if (IsInBounds(oNeighborItem))
 2201                     {
 2202 
 2203                         if (IsForeground(oNeighborItem))
 2204                         {
 2205 
 2206                             if (!oBoundaryPixels.Contains(oNeighborItem))
 2207                             {
 2208 
 2209                                 PixelType[oNeighborItem] = PixelTypes.Boundary;
 2210                                 oBoundaryPixels.Add(oNeighborItem);
 2211                             }
 2212                         }
 2213                     }
 2214                 }
 2215 
 2216                 if (CancelOperation)
 2217                 {
 2218                     goto CancelOperation;
 2219                 }
 2220             }
 2221         }
 2222     CancelOperation:
 2223 
 2224         AnimationPopState();
 2225         UpdateProgress(0, 0, 0);
 2226     }
 2227 
 2228 
 2229     //////////////////////////////////////////////////////////////////////////////////////////////
 2230     //// ANIMATION, PROGRESS BAR, AND STATUS METHODS
 2231     //////////////////////////////////////////////////////////////////////////////////////////////
 2232 
 2233     public void AnimationPushState()
 2234     {
 2235         _animationStateStack.Push(_animationState);
 2236     }
 2237 
 2238     public void AnimationPopState()
 2239     {
 2240         _animationState = (bool)_animationStateStack.Pop();
 2241     }
 2242 
 2243     private bool AnimationNow
 2244     {
 2245         get
 2246         {
 2247             //// Don't animate if animation disabled for this image //
 2248             if (_animation == AnimationStates.Disabled)
 2249             {
 2250                 return false;
 2251             }
 2252 
 2253             //// Don't animate if temporarily disabled //
 2254             if (_animationState == false)
 2255             {
 2256                 return false;
 2257             }
 2258 
 2259             //// Don't animate big images
 2260             if (_count > 2500)
 2261             {
 2262                 return false;
 2263             }
 2264 
 2265             return true;
 2266         }
 2267     }
 2268 
 2269 
 2270     public void AnimationOn()
 2271     {
 2272         _animationState = true;
 2273     }
 2274 
 2275 
 2276     public void AnimationOff()
 2277     {
 2278         _animationState = false;
 2279     }
 2280 
 2281 
 2282     private void AnimationRaisePixelScanned(Pixel pixel)
 2283     {
 2284         if (AnimationNow)
 2285         {
 2286             if (AnimationPixelScanned != null)
 2287             {
 2288                 AnimationPixelScanned(new Pixel(pixel), _animationLastScannedPixel);
 2289             }
 2290 
 2291             _animationLastScannedPixel = new Pixel(pixel);
 2292         }
 2293 
 2294         Application.DoEvents();
 2295     }
 2296 
 2297 
 2298     private void AnimationRaisePixelChanged(Pixel pixel)
 2299     {
 2300         if (AnimationNow)
 2301         {
 2302             if (AnimationPixelChanged != null)
 2303             {
 2304                 AnimationPixelChanged(new Pixel(pixel));
 2305             }
 2306         }
 2307 
 2308         Application.DoEvents();
 2309     }
 2310 
 2311 
 2312     private void AnimationRaiseImageChanged()
 2313     {
 2314         AnimationRaiseImageChanged(false);
 2315     }
 2316 
 2317     private void AnimationRaiseImageChanged(bool bForceEvent)
 2318     {
 2319         if (AnimationNow | bForceEvent)
 2320         {
 2321             if (AnimationImageChanged != null)
 2322             {
 2323                 AnimationImageChanged();
 2324             }
 2325         }
 2326 
 2327         Application.DoEvents();
 2328     }
 2329 
 2330 
 2331     public void UpdateProgress(int progressMin, int progressMax, int progressCount)
 2332     {
 2333         UpdateProgress(progressMin, progressMax, progressCount, false);
 2334     }
 2335 
 2336     public void UpdateProgress(int progressMin, int progressMax, int progressCount, bool refreshImage)
 2337     {
 2338 
 2339         if (progressMin < progressMax)
 2340         {
 2341 
 2342             if (progressCount < progressMin)
 2343             {
 2344                 progressMin = progressCount;
 2345             }
 2346             if (progressCount > progressMax)
 2347             {
 2348                 progressMax = progressCount;
 2349             }
 2350 
 2351             if (ProgressChanged != null)
 2352             {
 2353                 ProgressChanged(progressMin, progressMax, progressCount, refreshImage);
 2354             }
 2355         }
 2356 
 2357         else
 2358         {
 2359 
 2360             if (progressCount > progressMin)
 2361             {
 2362                 progressMin = progressCount;
 2363             }
 2364             if (progressCount < progressMax)
 2365             {
 2366                 progressMax = progressCount;
 2367             }
 2368 
 2369             progressCount = progressMin - progressCount;
 2370 
 2371             if (ProgressChanged != null)
 2372             {
 2373                 ProgressChanged(progressMax, progressMin, progressCount, refreshImage);
 2374             }
 2375         }
 2376 
 2377         Application.DoEvents();
 2378     }
 2379 
 2380 
 2381     //// Overloaded progress method using pixel from iterator //
 2382     public void UpdateProgress(Pixel pixel)
 2383     {
 2384         UpdateProgress(pixel, false);
 2385     }
 2386 
 2387     public void UpdateProgress(Pixel pixel, bool refreshImage)
 2388     {
 2389         if (ProgressChanged != null)
 2390         {
 2391             ProgressChanged(0, _count, pixel.ArrayIndex(_width), refreshImage);
 2392         }
 2393     }
 2394 
 2395 
 2396     public void UpdateStatus(string text)
 2397     {
 2398         if (StatusChanged != null)
 2399         {
 2400             StatusChanged(text);
 2401         }
 2402     }
 2403 
 2404 
 2405 
 2406     //// Enumerator for image class
 2407     //// Allows iteration through every pixel position in the image
 2408     private class ImagePixelEnumerator : IEnumerator
 2409     {
 2410 
 2411         private int lastModified;
 2412         private Pixel moCurrent;
 2413         private Image moParent;
 2414 
 2415 
 2416         public ImagePixelEnumerator(Image parent)
 2417         {
 2418             moParent = parent;
 2419             lastModified = parent.lastModified;
 2420             moCurrent = new Pixel(0, -1);
 2421             FileSystem.Reset();
 2422         }
 2423 
 2424 
 2425         object System.Collections.IEnumerator.Current
 2426         {
 2427             get
 2428             {
 2429                 if (moParent.lastModified != lastModified)
 2430                 {
 2431                     throw new InvalidOperationException("collection modified during iteration");
 2432                 }
 2433 
 2434                 moParent.AnimationRaisePixelScanned(moCurrent);
 2435 
 2436                 return moCurrent;
 2437             }
 2438         }
 2439 
 2440 
 2441         bool System.Collections.IEnumerator.MoveNext()
 2442         {
 2443             if (moParent.lastModified != lastModified)
 2444             {
 2445                 throw new InvalidOperationException("collection modified during iteration");
 2446             }
 2447 
 2448             Application.DoEvents();
 2449 
 2450             moCurrent.j = moCurrent.j + 1;
 2451             if (moCurrent.j >= moParent._width)
 2452             {
 2453                 moCurrent.j = 0;
 2454                 moCurrent.i = moCurrent.i + 1;
 2455 
 2456                 if (moCurrent.i >= moParent._height)
 2457                 {
 2458                     return false;
 2459                 }
 2460             }
 2461 
 2462             return true;
 2463         }
 2464 
 2465 
 2466         void System.Collections.IEnumerator.Reset()
 2467         {
 2468             if (moParent.lastModified != lastModified)
 2469             {
 2470                 throw new InvalidOperationException();
 2471             }
 2472 
 2473             moCurrent.i = 0;
 2474             moCurrent.j = -1;
 2475         }
 2476     }
 2477 
 2478 
 2479     System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 2480     {
 2481         return new ImagePixelEnumerator(this);
 2482     }
 2483 
 2484 
 2485     void System.Collections.ICollection.CopyTo(Array array, int index)
 2486     {
 2487         foreach (object value in this)
 2488         {
 2489             array.SetValue(value, index);
 2490             index = index + 1;
 2491         }
 2492     }
 2493 
 2494 
 2495     int System.Collections.ICollection.Count
 2496     {
 2497         get { return _count; }
 2498     }
 2499 
 2500 
 2501     bool System.Collections.ICollection.IsSynchronized
 2502     {
 2503         //// Ignore synchronization (for performance)
 2504         get { return false; }
 2505     }
 2506 
 2507 
 2508     object System.Collections.ICollection.SyncRoot
 2509     {
 2510         get { return this; }
 2511     }
 2512 }



Copyright (C) 2007-2009 by Robert Pinchbeck
All Rights Reserved