Extracting positive and negative shapes from a BitmapData
The first step in generating the "vector text effects" which I blogged about in my previous post is to grab a BitmapData of a character, then seperate the "characters components" into individual BitmapData instances.
So let's take a capital B :

The B only contains one "positive shape", that is, the "filled in" part. The B contains 3 "negative shapes" or the transparent bits. In this case we've got the surrounding area and the two "holes".
If we take a percent symbol "%"

We've got 3 positive shapes and 3 negative ones.
So how does one go about extracting these? I'm sure there are several approaches, I tried a few, this was the speediest of the ones I came up with.
Step 1 : Convert the captured BitmapData into one containing just two colors, a solid and a transparent. *Note:* this has the side effect that the extracted shapes have "pixelated" , "jagged" or "aliased" edges. For my purposes this is sufficient, a different approach would be needed to keep anti-aliasing.
-
public static function toMonoChrome(source:BitmapData,mono_color:uint=0xFF000000):BitmapData{
-
var bmd:BitmapData=source.clone();
-
bmd.threshold(bmd,bmd.rect,new Point(),">",0x00000000,mono_color);
-
return bmd;
-
}
Step 2 : Create an "inverse" of the "monochrome" image:
-
//create the monochrome for extracting "positive shapes"
-
var positive_two_color:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0x00);
-
positive_two_color.draw(source_bmd);
-
positive_two_color=BitmapDataUtil.toMonoChrome(positive_two_color,0xFFFF0000);
-
-
//create the monochrome for extracting "negative shapes"
-
var temp:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0xFF0000FF);
-
temp.draw(positive_two_color);
-
var negative_two_color:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0xFFFF0000);
-
negative_two_color.copyChannel(temp,positive_two_color.rect,new Point(), BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
Again, I'm sure there are other ways to skin this cat. I created a BLUE "temp" BitmapData, then drew the "positive shape" on it. I then copy the BLUE Channel into the ALPHA channel of a RED BitmapData, which creates an image with inverted RED and ALPHA from the "positive shape".
Step 3 : Extract the "component" BitmapDatas from the positive and negative BitmapDatas. This requires a few steps:
Create a loop which:
- finds the first non transparent pixel more info
- Does a FloodFill with Blue on that pixel
- Create a new BitmapData, and copy the BLUE Channel into it, add this to the "found" items
- Remove the Blue from the original by using a transparent FloodFill
Do this for both the "positive" and inverse "negative" BitmapDatas and store the discovered "component shapes" in a vector or an array.
Here's my code for doing so:
-
protected static function extractShapesFromMonochrome(bmd:BitmapData):Vector.<BitmapData>{
-
var scan_y:uint=0;
-
var non_trans:Point;
-
var found:Vector.<BitmapData>=new Vector.<BitmapData>();
-
var copy_bmd:BitmapData;
-
while(scan_y<bmd.height){
-
non_trans=BitmapDataUtil.getFirstNonTransparentPixel(bmd,scan_y);
-
if(non_trans==null)return found;
-
bmd.floodFill(non_trans.x,non_trans.y,0xFF0000FF);//fill with blue
-
copy_bmd=new BitmapData(bmd.width,bmd.height,true,0xFFFF0000);
-
copy_bmd.copyChannel(bmd,bmd.rect,new Point(),BitmapDataChannel.BLUE , BitmapDataChannel.ALPHA);
-
bmd.floodFill(non_trans.x,non_trans.y,0x00000000);//fill with blue
-
found.push(copy_bmd);
-
scan_y=non_trans.y;
-
}
-
return found;
-
}
That pretty much does it. The next step is to analyze the discovered shapes and generate vector points to work with... but that's another blog post.
Tags: flash as3 bitmapData


March 5th, 2009 at 4:13 pm
I’d be very interested in the approach you use for vectorizing bitmaps.
March 5th, 2009 at 4:19 pm
I’m gonna blog about a “simple shape extruder with the drawing API” next, then that should be followed by a post about the “vectorizing” process… hopefully sometime next week
May 12th, 2009 at 7:31 pm
[...] used my logo ? (as in ?? “node”) as the input image, run Sakri Rosenstrom’s image segmentation algorithm on it, dropped 10,000 random points into the segments, and drawn the minimum spanning tree of each [...]
January 31st, 2010 at 6:49 am
About the ‘pixel’ ‘jagged’ edges …
I think the ‘mask’ value might help….
var mask = 0x00FF0000;
bmd.threshold(bmd,bmd.rect,new Point(),”>”,0×00000000,mono_color,mask);
this code is not tested, but something like this , It worked for me when I was trying something very similar, and whatever it was , I gave up around the time that I was trying to make something just like this function that finds the first non transparent pixel ! wow thanx great help for this!