Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - scrdump.gif

Introduction

Ah! Marching ants! You've gotta love them! They add a sense of quality to your software. You know marching ants? When you select something in Photoshop, Paint Shop Pro or GIMP, the selected area is indicated by an animated dashed line. It looks like there are ants marching along that boundary.

But how on earth do you implement such beasts? This article will show you how to do it.

First attempt

When I wanted to implement marching ants, my first attempt was very simple. I created an array of SBytes. This array represented the selected area. The array was initialized with 0s. A function AddRectangle() changed values in this array to 1s. The 1s indicated the pixels which belonged to the selected area. To draw the outline of the selected area, I scanned the array from top to bottom and from left to right. At each pixel, I tested whether this pixel was an edge pixel or not. Testing if a pixel was an edge pixel is very simple: if the value of the current pixel was 1 and a neighboring pixel was 0, the pixel was an edge pixel. When an edge pixel was found, I set the the value of the array cell to 2.

Now, I had an array with all edge pixels. I converted them to paths. A path consisted of one ore more lines (if you select a rectangular area and you subtract another area inside that area, you need two paths, so in this case, we would have 2 paths with 4 lines). I wrote a function that could draw those paths with dashed lines. Using a timer, I drew each path with a different offset, so that the dashes seemed to move. My ants marched.

But I didn't like my solution that much. I wondered how Photoshop and other programs did it. My solution was also slow.

Second attempt

I downloaded the GIMP source code and studied it a bit. I started from scratch and this second solution is what you can download. The screenshot above also shows what to expect. Note that I only show how to implement marching ants, I don't provide a complete program or even a complete control.

Included in the source code is a program called Fotowinkel (fotowinkel.cs). This program creates a form and adds a PaintBox control to it. (If you wanted to create some sort of paint program, you could start using this control.) PaintBox provides scrollbars for the InnerPaintBox control. InnerPaintBox contains a PaintBoxImage which, in turn, contains a Selection object (Selection.cs). PaintBoxImage draws the background and centers the current image (you cannot specify an image, but this is easy to implement). Use the mouse to select rectangular areas. Each selected area will be added to the current selected area and marching ants will indicate the edges.

Selection.cs contains the selection engine, so to speak. The Selection object contains a bitmap, which is the selection mask. When you select a rectangle, the method AddRectangle() will draw a rectangle on the selection mask. GetOutline() is called at the end of the method. GetOutline() scans the selection mask from top to bottom and then again from left to right, searching for the edges of the current selected area. The lines are animated by drawing the lines using different texture brushes. In the constructor of the Selection object, you can see how I create 8 different texture brushes. The method Animate() will change the current brush when it's called. I borrowed this technique from the GIMP.

Some thoughts

What's the difference between my first attempt and my second? In the first attempt, I have to change the values of the selection mask to indicate what pixels make up the boundary of the selection. This is annoying, because, in practice, you would create a copy of the selection mask when updating the selection outline, which means, more memory (or you could keep an array of the old pixel values, but that would create other problems, such as detecting if an edge pixel already belongs to an edge or not). The advantage of this first technique is that the ants march clockwise, in the second solution definitely not.

Another problem is performance. On my (old) computer, it takes about 2 seconds to update the selection, and we're only talking about a 256x256 selection mask! GDI+ is slow. But using GDI using DllImport is no option because GDI+ uses anti-aliasing.

The demo project allows you to select rectangular areas, but the code is able to handle all kinds of shapes because the engine doesn't know about shapes, it only knows about pixels. If you want to experiment, write a method AddEllipse(Point x, Point y) or something like that and draw an ellipse on the selection mask bitmap. Call GetOutline() and you're done!

A final thought concerns the selection mask bitmap: I use new Bitmap() to create a selection mask, but in reality, this should be a grayscale bitmap.

Update (March 2, 2004)

I updated the source code. To speed up the GetOutline() method, I created a new class called SelectionMask. SelectionMask wraps a Bitmap and provides 4 methods to manipulate that bitmap: Lock(), GetPixel(), SetPixel() and Unlock(). GetPixel() and SetPixel() access the bitmap data directly using unsafe pointers. Wow! This really gives a "turbo boost".

I also added a method AddEllipse(). The demo project now has a menu where you can choose how to select an area: rectangular or elliptical.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalwhat if it's complex polygon? how do we march ant along the line with different slopes
scottchu.tw
18:53 9 Dec '09  
If the selection is complex polygon, how to find all interioir points? It seems that the simple outline scan in your source can't solve this case properly?

I want to march ant along the line slope like splashUp did(http://www.splashup.com/splashup/). What I can think of is implementing line algorithm & draw dashed or dotted line pattern by myself.

I'm currently using Flex & ActionScript. But they are lack of line pattern. Does C sharp have better graphic API? If it provides line pattern, does it do pattern along the line slope?
Generalfaster animate function
ShyH
0:55 15 Nov '05  
you can use the %(mod) operator instead of if to speed Animate func.

public void Animate(Graphics g, Point offset)
{
DrawOutline(g, offset);
_CurMAB++;
_CurMAB %= 8;
}


Rose

-- modified at 5:55 Tuesday 15th November, 2005
GeneralIs it a bug?
rutmir
3:42 14 Nov '05  
Hi! Thanks for so cool control! I've included it in my project. Wink But when I was importing your code in mine, I found some a bug in OnPaintBackground of the InnerPaintBox class. This bug raised when I used non rectangle bitmaps to show in the InnerPaintBox. Instead of:
// top
h = (clientsize.Height - imgsize.Width) / 2;
// middle left
y = (clientsize.Height - imgsize.Width) / 2;
// middle right
y = (clientsize.Height - imgsize.Width) / 2;
// bottom
y = (clientsize.Height - imgsize.Width) / 2 + imgsize.Height;
h = (clientsize.Height - imgsize.Width) / 2 + 1; // 7 / 2 = 3 + 4

you should change all the widths to heights.
With best regards, Ruslan Laugh
GeneralRe: Is it a bug?
q123456789
5:53 14 Nov '05  
Nice to hear you can use it. I asked thecodeproject to update the download file with the modifications.
GeneralAdding straight line
Craig D.
17:01 25 Sep '05  
This is awesome, great job. The elipse was a nice touch, can you tell me how too add straight lines too. If it's easier, I could start with a rectangle and intersect two points with a straight line that pivits from the origin. Thanks
GeneralRe: Adding straight line
Anonymous
23:30 25 Sep '05  
Add a method AddLine() to Selection (in selection.cs). Take a look at AddEllipse() and AddRectangle(): they just draw a black shape (whatever) on the selection mask. That's all there is to it. Of course, it's up to you to implement the mousing stuff. You also have to add a SelectionStyle and change the method UpdateSelection() (in Fotowinkel.cs).
GeneralRe: Adding straight line
Craig D.
5:42 26 Sep '05  
Thanks, after I posted the message I did exactly that. So I have a line function now to. Do you want me to posted the code or email it to you??

What I need to look into now is how to determine if a pixel is inside or outside the selection area to do a crop. I think there is already some functionality to do that test. Then maybe display an anchor at each corner point and be able to move it. Not sure how complex I want to get.

GeneralRe: even more Secret. (Color cycling)
Anonymous
23:13 14 Apr '04  
This is a nineties theory. DOS was still used in those days. When you changed the values in the palette (256 colors), the colors on the screen immediately changed too. This technique could be used in a game for explosions or so. But now we're in the vincents (2000+). What palette would you want to change? We use 24 colors. You would have to use a 256 indexed color bitmap. And you would have to call Invalidate() anyway to update the window. And even if you had access to "the system palette", you would change ALL colors of ALL windows on the screen, not just the colors of the ants.
Generaleven more Secret. (Color cycling)
Dnt
14:27 14 Apr '04  
I dont know if this is possible, but i think someone is smarter then me and can implement this.. Smile.. this is a solution i learned from the "demo" community back in the early 1990´s.

The logic behind the solution is that its faster to change one color value in the grafic-cards DAC (or the Bitmap pallet) then to change many individual pixels.

As im a beginner to CSharp and its syntax i´ll explain in "pseudo" C#.. Smile

// make a Array with colors, length = 2 times the length of the "ant"
// in pixels -1 (zeroBased), in this example ant.Length = 4
Color[] colors = new Color[7];

// init. colors. 1/2 Black, 1/2 White
for (Int32 i=0; i<8 ; i++)
{
if (i<4) { colors[i] = System. (0,0,0) }
else { colors[i] = System. (255,255,255) }
}

// now we have a pallett looking like this:
color 0: black
color 1: black
color 2: black
color 3: black
color 4: white
color 5: white
color 6: white
color 7: white

// the Color pattern for the texture brush
String pattern = new String;
pattern = "01234567" +
"12345670" +
"23456701" +
"34567012" +
"45670123" +
"56701234" +
"67012345" +
"70123456";


Now, use the pattern to fill the edges.

To get the "ants" to move all we have to do is to itterate over the first 4 colors.

loop
{
// change Black to White
for (Int32 i=0; i<4 ; i++)
{
colors[i] = System. (255,255,255);
colors[i+4] = System. (0,0,0);
}
// change white to black
for (Int32 i=0; i<4 ; i++)
{
colors[i] = System. (0,0,0);
colors[i+4] = System. (255,255,255);
}
}


Thats it I guess.. the ants should appere to "move" now and the performace will not be limited by the lenght(pixels) of the edge, in theory..

Thoughts are Contagious
A meme is a contagious pattern of information that replicates by symbiotically infecting human minds and altering their behavior, causing them to propagate the pattern.
GeneralAnother Attempt
jbialek
0:14 25 Mar '04  
Hi you all, I figued another solution:
I created a TextureBrush based on an Image dynamically created (white background, black bar wandering from left to right).
Then I rotated the Texturebrush and voilà there are those marching ants Smile

Code: WTF

int blockpos = tick %10; // tick is an int constantly beeing increased
Bitmap block = new Bitmap(10,10);
Graphics blockg = Graphics.FromImage(block);
blockg.Clear(Color.White);
blockg.FillRectangle(Brushes.Black,blockpos,0,5,10);
blockg.FillRectangle(Brushes.Black,blockpos-10,0,5,10);
blockg.Dispose();

TextureBrush b = new TextureBrush(block);
b.RotateTransform(45);
//b.ScaleTransform(0.5f,0.5f); // You might want this
Pen p = new Pen(b,1);

// Do your path drawing here

block.Dispose();


C is sharp
GeneralAnother marching ants technique: DashOffset
Frank Hileman
5:48 2 Mar '04  
The Pen class has a property called DashOffset. By incrementing this property cyclicly, you can achieve a marching ants effect on any shape you draw with that Pen. In this screenshot of a VG.net animation demo, you can see how we used DashOffset to illustrate flow through a pipe. This pipe is straight, but you can use the effect on curved shapes as well:

http://www.vgdotnet.com/build_reuse_meter.shtml#animation_sample

-Frank

check out VG.net: www.vgdotnet.com
An animated vector graphics system integrated in VS.net
Generalslightly slow response
Joel Holdsworth
6:56 1 Mar '04  
Just tried out your demo - the ants certainly march quite nicly... but after drawing a rectangle the program locks for about a second on my 2.6GHz Pentium. Is there any way of improving the performance here?

Joel Holdsworth
GeneralRe: slightly slow response
q123456789
0:03 2 Mar '04  
Hi,

yes, on my computer it took about 2 seconds. I've just profiled the code and as expected, the performance hit is in GetPixel() and SetPixel(). I updated the source code and now I use unsafe pointers to manipulate the bitmap directly. Now it takes about 1 second on my computer, so 2x faster!
GeneralRe: slightly slow response
Matthew Hazlett
14:43 2 Mar '04  
Much Better..
I tried this awhile ago and it was very slow but now it seems to be better Smile

Good job...



Matthew Hazlett
Windows 2000/2003 MCSE
Never got an MCSD, go figure...


Last Updated 14 Nov 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010