Accessibility

Table of Contents

Pixel Bender basics for Flash

Using blends

A blend combines the colors in the display object to which the blend is applied with the colors below the object on the Stage. Flash Player supports several built-in blends, defined in the BlendMode class. As a learning exercise, we duplicate a couple of the built-in blends. Then we create a blend that can't be easily achieved using the built-in options.

To apply a blend to a display object, create a Shader object with the loaded kernel bytecode and assign it to the blendShader property of the display object. A blend kernel must have two inputs. The first input is the foreground display object (whose blendShader property is set). The second input is whatever is behind the foreground object. If you use additional inputs, perhaps for creating masks or textures, you must assign an image in the form of a BitmapData object to these inputs yourself before applying the blend.

Multiply

In a multiply blend, each color in the foreground object is multiplied by the color of the background object. This blend darkens the result, except in the scenario where one of the images is pure white:

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player

The following kernel declares two inputs, named foreground and background, and an output, named result. In the evaluatePixel() function, the pixel at the current coordinate is sampled in each image using the sampleNearest() function. The pixels are then multiplied together.

<languageVersion : 1.0;>

kernel MultiplyBlend
<   namespace : "com.adobe.example";
    vendor : "Adobe Systems Inc.";
    version : 1;
    description : "A simple multiply blend";
>
{
    input image4 foreground;
    input image4 background;
    output pixel4 result;

    void evaluatePixel()
    {
        pixel4 a = sampleNearest( foreground, outCoord() );
        pixel4 b = sampleNearest( background, outCoord() );
        result = a * b;
    }
}
          

Note: When you multiply two vectors in the Pixel Bender language, each corresponding pair of component values are multiplied together. Thus, if a and b are float2 vectors, for example, the statement:

a * b

is equivalent to the two statements:

a.x * b.x 
a.y * b.y
          

The following ActionScript code is used to load and apply the blend:

var camellia_mc:MovieClip;

//Embed the Pixel Bender kernel in the output SWF
[Embed(source="multiplyblend.pbj", mimeType="application/octet-stream")]
var MultiplyBlendKernel:Class;

var shader:Shader = new Shader( new MultiplyBlendKernel() );
camellia_mc.blendShader = shader;
          

Screen

A screen blend inverts the colors, multiplies them together, and then inverts the result. This has the opposite effect as applying the multiply blend. The screen blend lightens the result, except in the scenario where one of the images is black:

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player
<languageVersion : 1.0;>

kernel ScreenBlend
<   namespace : "com.adobe.example";
    vendor : "Adobe Systems Inc.";
    version : 1;
    description : "Screen blend";
>
{
    input image4 foreground;
    input image4 background;
    output pixel4 result;
                        
    void evaluatePixel()
    {
        pixel4 a = sampleNearest( background, outCoord() );
        pixel4 b = sampleNearest( foreground, outCoord() );

        result = 1.0 - (1.0 - a) * (1.0 - b);
        
    }
}
          

As you can see, this kernel is almost identical to the multiply kernel. Only the mathematical operation used to produce the result has changed. This example uses the same ActionScript code, except it is loading and applying a different shader.

Hard light

A hard light blend is a combination of the multiply and screen blends. If the foreground pixel is lighter than 50% gray, then a screen blend is performed. Otherwise, a multiply blend is performed:

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player
<languageVersion : 1.0;>

kernel HardLightBlend
<   namespace : "com.adobe.example";
    vendor : "Adobe Systems Inc.";
    version : 1;
    description : "Hard light blend";
>
{
    input image4 foreground;
    input image4 background;
    output pixel4 result;

    void evaluatePixel()
    {
        pixel4 a = sampleNearest( background, outCoord() );
        pixel4 b = sampleNearest( foreground, outCoord() );

        float gray = (b.r + b.g + b.b)/3.0;
        
        if( gray < 0.5 )
        {
            result = 2.0 * a * b;
        } 
        else
        {
            result = 1.0 - 2.0 * (1.0 - a) * (1.0 - b);
        }
    }
}
          

The hard light blend is a bit more complicated than the previous two. First, the gray level of the pixel in the foreground image is calculated by averaging the color channels. Then, an if statement is used to select either a multiply or a screen blend operation.

Again, this example uses the same ActionScript code, but it loads and applies a different shader.

Perlin grain

Now for something that is a bit more difficult to achieve with the built-in blends. The next filter uses a noise texture and sin() functions to generate a wood grain or marbling effect. The effect depends on the characteristics of the noise texture and generally looks best with Perlin-type noise:

AlertThis content requires Flash

Download the free Flash Player now!

Get Adobe Flash Player

The shader works by sampling the pixels values from the noise image. Instead of using the noise pixels directly in the image, the shader feeds the noise value into a series of sin() functions. The background is multiplied by the result. A turbulence parameter is used to control the curviness of the resulting effect:

<languageVersion: 1.0;>

kernel GrainBlend
<   namespace : "com.adobe.example";
    vendor : "Adobe Systems Inc.";
    version : 1;
    description : "Creates a wood grain or marbling effect"; >
{
    input image4 background;    
    input image4 noise;

    output pixel4 dst;
    parameter float turbulence
    <
        maxValue : 500.0;
        minValue : 0.0;
        defaultValue : 150.0;
    >;

    void evaluatePixel()
    {   
        pixel4 a = sampleNearest(background, outCoord());
        pixel4 b = sampleNearest(noise, outCoord());
        
        
        float alpha = a.a; //save the original alpha

        if( (b.a > 0.0) && (a.a > 0.0)){
            float seed = outCoord().x + (((b.r + b.g + b.b)/3.0)  * turbulence);

            float grain = (0.7 * sin(seed) + 0.3 * sin(2.0 * seed + 0.3) + 0.2 * sin(3.0 * seed + 0.2));
            dst = sampleNearest(background, outCoord()) * (grain + 0.5);
            dst.a = alpha; //restore the original alpha           
        }
        else {
            //Just copy the background pixel outside the area of the noise image
            dst = sampleNearest(background, outCoord());
        }

    }
}
          

The ActionScript code used for this example loads and applies the shader in the same way as the previous examples. In addition, the example creates a slider to control the turbulence parameter:

import fl.controls.Slider;
import fl.events.SliderEvent;

var noise_mc:MovieClip;
var turbulence:Slider;

//Embed the Pixel Bender kernel in the output SWF
[Embed(source="grainblend.pbj", mimeType="application/octet-stream")]
var GrainBlendKernel:Class;

//Create the Shader object
var shader:Shader = new Shader( new GrainBlendKernel() );
	
//Set the slider values based on the parameter metadata
turbulence.minimum = shader.data.turbulence.minValue;
turbulence.maximum = shader.data.turbulence.maxValue;
turbulence.value   = shader.data.turbulence.defaultValue;
turbulence.liveDragging = true;
turbulence.addEventListener( SliderEvent.CHANGE, updateFilter );

//Apply the blend
noise_mc.blendShader = shader;

function updateFilter( event:SliderEvent ):void
{
	shader.data.turbulence.value = [turbulence.value];
	noise_mc.blendMode = BlendMode.NORMAL;
	noise_mc.blendShader = shader;
}
          

The example uses a slider to control the shader turbulence parameter. The minimum, maximum and starting values of the slider are set according to the parameter metadata. Then, the updateFilter() method is used to change the parameter value whenever a change event is dispatched by the Slider object. Because a shader object is cloned when you set the blendShader property of a display object, you cannot simply change the parameter value of the original Shader object. You must also reassign the updated Shader object to the blendShader property.

For simplicity, this example uses a bitmap for the noise texture. You can also use the perlinNoise() function of the BitmapData class to create a suitable texture.