Even though Pixel Bender filters are meant and used to alter pixels, what they really do is applying math operations to numbers. When we use pixel bender filters in Flash, we can actually use them to process numbers or to alter an image. We'll start by looking at an example with number processing and then we'll move on to pixel manipulation.

To be able to load a Pixel Bender file in Flash, it needs first to be compiled in a .pbj format and we can do so by selecting "Export filter for Flash Player" in our Pixel Bender Toolkit. I created a simple Pixel Bender filter that will allow us to manipulate numbers, here is how this looks:

<languageVersion : 1.0;>

kernel NewFilter
< namespace : "red divider";
vendor : "aswebcreations.com";
version : 1;
description : "simple dividing on red channel";
>
{
input image1 src;
output pixel3 dst;

void
evaluatePixel()
{
pixel1 temp = sample(src,outCoord());
pixel1 divided = temp/2.0;
pixel1 squared = sqrt(temp);
dst = pixel3(temp,divided,squared);
}
}

Now you can see that our image input is of type 1 which means it's expecting one number instead of 3 for a regular pixel (rgb) or 4 for rgba pixels. Our output is pixel3 based and this is a minimum for an output in Pixel Bender (as far as I know). Then in the evaluatePixel() method we get our number and store it in a variable:

pixel1 temp = sample(src,outCoord());

Then we store the result of dividing by 2 in another variable:

pixel1 divided = temp/2.0;

And finaly the result of squaring our number in another variable:

pixel1 squared = sqrt(temp);

We of course then pass this result to our output:

dst = pixel3(temp,divided,squared);

You can of course apply this filter to a picture but its real purpose is to process numbers so let's get started with our actionscript code now:

var loader:URLLoader = new URLLoader();
    loader.dataFormat = URLLoaderDataFormat.BINARY;
    loader.addEventListener(Event.COMPLETE, onLoadComplete);
    loader.load(new URLRequest("divider.pbj"));
     
function onLoadComplete(event:Event):void {

}

This loads our filter in Flash what we need now is create an instance of the Shader class and get our filter data ready:

var shader:Shader;
function onLoadComplete(event:Event):void {
shader = new Shader(loader.data);
}

Our filter is now ready to be used! There's 3 data types we can use as Shader input and ouput, BitmapData, byteArray, and Vector.<Number> (note that some filters do not require inputs). I've seen already examples on the web about processing numbers using ByteArray so I thought I would use Vector for this tutorial instead. I'm going to create a Vector and populate it with random numbers then set it as the shader input. Then I'll create another vector that I'll use to store the results coming from our filter (Note that it's possible to use the same object as input and output in which case results are building up on preceding results which can be interesting for bitmap alteration for example).

function onLoadComplete(event:Event):void {
shader = new Shader(loader.data);
var vector:Vector.<Number> = new Vector.<Number>();
for(var i:Number = 0; i<9; i++){
    vector.push(Math.random()*500);   
    }
shader.data.src.width = 3;
shader.data.src.height = 3;
shader.data.src.input = vector;
var vector2:Vector.<Number> = new Vector.<Number>();
var shader_job:ShaderJob = new ShaderJob(shader, vector2, 3, 3);
shader_job.start(true);
var txt:TextField = new TextField();
txt.wordWrap = true;
txt.width = stage.stageWidth;
txt.height = stage.stageHeight;
addChild(txt);
for(i = 0; i<vector2.length; i+=3){
    txt.appendText((vector2[i]+" divided by 2 = "+vector2[i+1]+" and squared = "+vector2[i+2]+"\n"));
    }
}

Here is the result:



Let's look at the code:

var vector:Vector.<Number> = new Vector.<Number>();
for(var i:Number = 0; i<9; i++){
vector.push(Math.random()*500);
}
shader.data.src.width = 3;
shader.data.src.height = 3;
shader.data.src.input = vector;

Here we simply create a Vector.<Number> and populate it with some random numbers (9 numbers in this case). Then we pass some values to our shader input called "src" (defined in the pixel bender). We indicate the size of the input with src.width = 3 and src.height = 3 (which means 9) and finally pass our Vector as the input: src.input = vector.

var vector2:Vector.<Number> = new Vector.<Number>();
var shader_job:ShaderJob = new ShaderJob(shader, vector2, 3, 3);
shader_job.start(true);

We create a new Vector and pass it as the output in the ShaderJob instance. We pass along the Shader we want to use of course and the size of the ouput we are expecting (3,3). The rest of the code creates a text field to display our result. Then:

for(i = 0; i<vector2.length; i+=3){
txt.appendText((vector2[i]+" divided by 2 = "+vector2[i+1]+" and squared = "+vector2[i+2]+"\n"));
}

That's right, we didn't get 9 numbers back but 27! Remember our output is of type pixel3 so that means 3 numbers for each number that we process!

A ShaderJob instance uses the GPU to process the Pixel Bender filter so this leaves the CPU take care of normal stuff in our Flash applications while the Pixel Bender is running. This creates a multithreaded Flash application! Passing true to the start method makes Flash wait for the result to get back while passing false (or nothing) makes the results asynchronous. You can then add an event listener to your ShaderJob instance and handle the results in your event handler. Now we could make our filter more dynamic by adding a parameter:

input image1 src;
output pixel3 dst;
parameter float div;

pixel1 divided = temp/div;//in the evaluatePixel() method

We now added a parameter in our filter which allows us to change on the fly some value like here the number we are dividing by (defaults to zero). We would rewrite our actionscript code that way:

shader.data.src.width = 3;
shader.data.src.height = 3;
shader.data.src.input = vector;
shader.data.div.value = [6];

Note the array style value. All values passed to parameters must be in array form. Now if we were to save our filter and add this new line of code then our numbers would be divided by 6. We could also of course tween this value and get our results back in real time. In Pixel Bender we can also provide default values, minimum values and maximum values to our parameters which we can then retrieve in our Flash application. But let's leave this for now and start experimenting with bitmap manipulation.