# Comparing Filters in Flash

In this tutorial, we’ll create a filter program which will allow the user to apply different filters to an image, change intensity levels and compare the original and filtered images. We’ll also be using components for our controls.

## Brief Overview

There’ll be three layers: one for the ActionScript, another for the text and components and the last for the image. The compiled program will contain two images since one will be created at run-time. The run-time version will have filters applied to it. In addition, a mask will be created at run-time which will be used to show/hide parts of the filtered image.

I’m assuming you already have a basic understanding of Flash and ActionScript 3.0. However, I’ll still try to keep each step as clear as possible.

Note: Throughout this tutorial I’ll be using weak references when adding event listeners. For more information about weak references, refer to this blog post.

## Step 1

Create a new ActionScript 3.0 document. Set the Stage size to 600 x 500px. I’ve chosen this size since the image I’ll be using will fit comfortably within these dimensions.

## Step 2

Create the three previously mentioned layers(namely “actions”, “text and components” and “image (original)”). Lock the “actions” layer since you won’t add anything to that layer apart from ActionScript.

## Step 3

Select the “text and components” layer and add the static text “Filter” and “Intensity” to the top left corner.

## Step 4

Go to the Components panel and drag five buttons and two sliders onto the stage. You can basically lay them out anyway you like.

Give the buttons label values of "Default", "Blur", "Emboss", "Brightness", and "Negative". Then give them instance names of "default_btn", "blur_btn", "emboss_btn", "brightness_btn", and "negative_btn".

Give the intensity slider an instance name of "intensity_sld", then name the mask slider "mask_sld".

Note: It’s not too important where you place the mask slider since we’ll use ActionScript to reposition it later.

Terminology: Throughout this tutorial I may refer to "mask slider" or "mask_sld" interchangeably. The same goes for "intensity slider" and "intensity_sld".

## Step 5

You can now lock the "text and components" layer. Next, select the "image (original)" layer and import an image you want to use. I’m going to use a photo of a dandelion.

## Step 6

Select the image and convert it into a Movie Clip symbol. I’ve named it "dandelion", but it can be anything you like. Make sure the registration point is at the top left corner. Go into the "Advanced" view and check "Export for ActionScript". The "Export in frame 1" should automatically be checked. For the class, enter "PhotoDandelion" and leave the base class as "flash.display.MovieClip".

## Step 7

Give the new image Movie Clip the instance name "photoOriginal". You can now lock the "image (original)" layer.

## Step 8 – Filter Basics

Flash allows you to apply filters similar to the ones in Photoshop. You can apply filters from the Properties panel or via ActionScript. Here are some of the basic filters Flash provides: bevel, blur, drop shadow, glow, etc.

Using ActionScript we can apply a filter to any DisplayObject through its "filters" property. The value assigned to the "filters" property needs to be an array. You can also apply more than one filter.

// Applying a blur filter to a DisplayObject.
var bFilter:BlurFilter = new BlurFilter();
var arrFilters:Array = [bFilter];
displayObj.filters = arrFilters;


## Step 9 – Convolution Filters

Flash also supports convolution filters that can perform more advanced effects such as brightness, edge detection, embossing, etc.

Convolution filters can only be applied through ActionScript and also require the use of matrices which describe how the colours for each pixel should be effected. The matrix needs to be in an array.

// Default
var matrixDefault:Array = [0, 0, 0,
0, 1, 0,
0, 0, 0];

// Brighter
var matrixBrighter:Array = [0, 0, 0,
0, 2, 0,
0, 0, 0];

// Darker
var matrixDarker:Array = [0, 0,   0,
0, 0.5, 0,
0, 0,   0];

// Emboss
var matrixEmboss:Array = [-1, -1, 0,
-1,  1, 1,
0,  1, 1];


The center value is the current pixel and the surrounding values are its neighbouring pixels. The default value is 1; increasing this value will cause the image to be brighter. Decreasing it will cause it to be darker. Having negative values on the top left and positive values on the bottom right (or vice versa) creates an emboss effect.

Using ActionScript, we can apply a convolution filter like so:

// The following matrix will brighten the image.
var matrixBrighter:Array = [0, 0, 0,
0, 2, 0,
0, 0, 0];
var matrixCol:Number = 3;	// Number of column
var matrixRow:Number = 3;	// Number of row
var cvFilter:ConvolutionFilter = new ConvolutionFilter(matrixCol, matrixRow, matrixBrighter);
var arrFilters:Array = [cvFilter];
displayObj.filters = arrFilters;


## Step 10

Now we’re going to write the ActionScript code. Select the first frame of the "actions" layer and go into the Actions panel.

## Step 11

Normally I like to keep the text consistent in the whole program. So here’s the code for setting the text (Verdana) for all the components globally.

import fl.managers.StyleManager;
var format:TextFormat = new TextFormat("Verdana", 10, 0x000000);
StyleManager.setStyle("textFormat", format);


## Step 12

Now let’s create a copy of the photo (for applying filters) and position it to the same location as the original photo. I’m going to use version B, since I want to apply a mask after the filter. With version A, if you apply a blur filter you may notice that the blur exceeds the mask. That’s because the filter is applying its effect after the mask.

Later on we’ll mask "photoWrapper" and only apply the filters to "photoEffected".

/*
// Version A
var photoEffected:PhotoDandelion = new PhotoDandelion();
photoEffected.x = photoOriginal.x;
photoEffected.y = photoOriginal.y;
*/

// Version B
var photoWrapper:Sprite = new Sprite();
photoWrapper.x = photoOriginal.x;
photoWrapper.y = photoOriginal.y;
var photoEffected:PhotoDandelion = new PhotoDandelion();


## Step 13

Next create the line that will separate the filtered and original photo. We’ll use a shape to draw a line. Position the line to the bottom middle of the photo, then draw the line from bottom up.

var comparisonLine:Shape = new Shape();
comparisonLine.x = photoOriginal.x + photoOriginal.width/2;
comparisonLine.y = photoOriginal.y + photoOriginal.height;
comparisonLine.graphics.lineStyle(2, 0x999999, 1, true);
comparisonLine.graphics.moveTo(0, 0);
comparisonLine.graphics.lineTo(0, -photoOriginal.height);


## Step 14

You’ll recall that we have a mask slider. Using ActionScript, we can position it underneath the photo and stretch it so its width is the same as the photo width. Setting the "maximum" and "width" values to the photo’s width help create a 1:1 ratio. We therefore won’t need to create any scale conversion later on. Additionally, we’ll center the arrow in the slider.

mask_sld.x = photoOriginal.x;


## Step 15

Now let’s create the function and assign the event for the mask slider. We want the "comparisonLine" to follow the mask slider. The "photoMask" width should change according to the mask slider whenever it’s being dragged. I know we haven’t addressed "photoMask" yet, but that’s the next step.

import fl.events.SliderEvent;

comparisonLine.x = evt.target.value + evt.target.x;
}



## Step 16

As mentioned before, "photoMask" will be used to mask the "photoWrapper" as opposed to "photoEffected". Since it’s going to be a rectangular mask, we’ll use a shape. Set the position and dimension of the rectangle to the same as "photoOriginal". Make sure you fill the shape with a colour (this can be any colour) or else the mask may not work as expected. Then set "photoMask" as photoWrapper’s mask and the width of the mask to the mask slider’s "value".

var photoMask:Shape = new Shape();



## Step 17

We need a way to store the last button pressed, which we’ll store in "activeFilter". This will be used later by the intensity slider.

var activeFilter:Button;


## Step 18

Next we’ll move on to the intensity slider. We define the function "intensityChange" which will be called whenever the intensity slider is dragged. The function will dispatch an event (i.e. mouse click) based on the last button pressed. We’ll also assign the event and initially disable the slider.

function intensityChange(evt:SliderEvent):void  {
activeFilter.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
intensity_sld.enabled = false;


## Step 19

Now let’s define the filter functions. The "default" and "negative" filter functions are similar in that they both disable the intensity slider and thus do not need to update the slider’s properties. For "blur", "emboss", and "brightness", they all need to re-enable the intensity slider. Additionally, they use the "value" property to determine the intensity of the filter. Also, each filter has its own unique "maximum" value, so we need to recalculate the "snapInterval". The "value" property is also recalculated since I’d like the slider to reset half way between the min and max. We only need to set the intensity slider properties when the "activeFilter" is not equal to the current target.

function default_btnHandler(evt:MouseEvent):void  {
var matrixDefault:Array = [0, 0, 0,
0, 1, 0,
0, 0, 0];

var conv:ConvolutionFilter = new ConvolutionFilter(3, 3 , matrixDefault);
photoEffected.filters = [conv];

activeFilter = evt.target as Button;
intensity_sld.enabled = false;
}

function blur_btnHandler(evt:MouseEvent):void  {
if(activeFilter != evt.target)  {
intensity_sld.maximum = 30;
intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;
intensity_sld.value = intensity_sld.maximum/2;
}

var blurX:Number = intensity_sld.value;
var blurY:Number = intensity_sld.value;
photoEffected.filters = [new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH)];

activeFilter = evt.target as Button;
intensity_sld.enabled = true;
}

function emboss_btnHandler(evt:MouseEvent):void  {
if(activeFilter != evt.target)  {
intensity_sld.maximum = 4;
intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;
intensity_sld.value = intensity_sld.maximum/2;
}

var matrixEmboss:Array = [-intensity_sld.value, -intensity_sld.value, 0,
-intensity_sld.value, 1, intensity_sld.value,
0, intensity_sld.value, intensity_sld.value];

var conv:ConvolutionFilter = new ConvolutionFilter(3, 3, matrixEmboss);
photoEffected.filters = [conv];

activeFilter = evt.target as Button;
intensity_sld.enabled = true;
}

function brightness_btnHandler(evt:MouseEvent):void  {
if(activeFilter != evt.target)  {
intensity_sld.maximum = 6;
intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;
intensity_sld.value = intensity_sld.maximum/2;
}

var matrixBrightness:Array = [0, 0, 0,
0, intensity_sld.value, 0,
0, 0, 0];

var conv:ConvolutionFilter = new ConvolutionFilter(3, 3, matrixBrightness);
photoEffected.filters = [conv];

activeFilter = evt.target as Button;
intensity_sld.enabled = true;
}

function negative_btnHandler(evt:MouseEvent):void  {
var matrixNegative:ColorMatrixFilter = new ColorMatrixFilter([-1,  0,  0, 0, 255,
0, -1,  0, 0, 255,
0,  0, -1, 0, 255,
0,  0,  0, 1,   0]);
photoEffected.filters = [matrixNegative];

activeFilter = evt.target as Button;
intensity_sld.enabled = false;
}


Note: For the "negative" filter, I’ve used a ColorMatrixFilter which uses a 4 by 5 matrix to apply colour transformations. For more information about the ColorMatrixFilter, refer to this Adobe Flash Article.

## Step 20

Now let’s assign each of the buttons to their corresponding functions.

default_btn.addEventListener(	MouseEvent.CLICK, default_btnHandler,    false, 0, true);
blur_btn.addEventListener(		MouseEvent.CLICK, blur_btnHandler, 	  	 false, 0, true);
emboss_btn.addEventListener(	MouseEvent.CLICK, emboss_btnHandler, 	 false, 0, true);
negative_btn.addEventListener(	MouseEvent.CLICK, negative_btnHandler,   false, 0, true);


## Step 21 – Final code

I’ve created three extra functions namely "applyConvolutionFilter", "activeFilterAndIntensityOnOff", and "setSliderProperties". Their purpose is to reduce repetitive coding in the filter functions.

Your final complete code should look similar to this…

import fl.events.SliderEvent;
import fl.managers.StyleManager;

var format:TextFormat = new TextFormat("Verdana", 10, 0x000000);
StyleManager.setStyle("textFormat", format);

var photoWrapper:Sprite = new Sprite();
photoWrapper.x = photoOriginal.x;
photoWrapper.y = photoOriginal.y;
var photoEffected:PhotoDandelion = new PhotoDandelion();

var comparisonLine:Shape = new Shape();
comparisonLine.x = photoOriginal.x + photoOriginal.width/2;
comparisonLine.y = photoOriginal.y + photoOriginal.height;
comparisonLine.graphics.lineStyle(2, 0x999999, 1, true);
comparisonLine.graphics.moveTo(0, 0);
comparisonLine.graphics.lineTo(0, -photoOriginal.height);

comparisonLine.x = evt.target.value + evt.target.x;
}

var activeFilter:Button;

function intensityChange(evt:SliderEvent):void  {
activeFilter.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
intensity_sld.enabled = false;

function applyConvolutionFilter(matrix:Array):void  {
var cFilter:ConvolutionFilter = new ConvolutionFilter(3, 3 , matrix);
photoEffected.filters = [cFilter];
}
function activeFilterAndIntensityOnOff(btn:Button, intensityOnOff:Boolean):void  {
activeFilter = btn;
intensity_sld.enabled = intensityOnOff;
}
function setSliderProperties(btn:Button, max:Number):void  {
if(activeFilter != btn)  {
intensity_sld.maximum = max;
intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;
intensity_sld.value = intensity_sld.maximum/2;
}
}

function default_btnHandler(evt:MouseEvent):void  {
var matrixDefault:Array = [0, 0, 0,
0, 1, 0,
0, 0, 0];
applyConvolutionFilter(matrixDefault);
activeFilterAndIntensityOnOff(evt.target as Button, false);
}

function blur_btnHandler(evt:MouseEvent):void  {
var btn:Button = evt.target as Button;
setSliderProperties(btn, 30);
var blurX:Number = intensity_sld.value;
var blurY:Number = intensity_sld.value;
photoEffected.filters = [new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH)];
activeFilterAndIntensityOnOff(btn, true);
}

function emboss_btnHandler(evt:MouseEvent):void  {
var btn:Button = evt.target as Button;
setSliderProperties(btn, 4);
var matrixEmboss:Array = [-intensity_sld.value, -intensity_sld.value, 0,
-intensity_sld.value, 1, intensity_sld.value,
0, intensity_sld.value, intensity_sld.value];
applyConvolutionFilter(matrixEmboss);
activeFilterAndIntensityOnOff(btn, true);
}

function brightness_btnHandler(evt:MouseEvent):void  {
var btn:Button = evt.target as Button;
setSliderProperties(btn, 6);
var matrixBrightness:Array = [0, 0, 0,
0, intensity_sld.value, 0,
0, 0, 0];
applyConvolutionFilter(matrixBrightness);
activeFilterAndIntensityOnOff(btn, true);
}

function negative_btnHandler(evt:MouseEvent):void  {
var matrixNegative:ColorMatrixFilter = new ColorMatrixFilter([-1,  0,  0, 0, 255,
0, -1,  0, 0, 255,
0,  0, -1, 0, 255,
0,  0,  0, 1,   0]);
photoEffected.filters = [matrixNegative];
activeFilterAndIntensityOnOff(evt.target as Button, false);
}

default_btn.addEventListener(   MouseEvent.CLICK, default_btnHandler,    false, 0, true);
blur_btn.addEventListener(      MouseEvent.CLICK, blur_btnHandler,       false, 0, true);
emboss_btn.addEventListener(    MouseEvent.CLICK, emboss_btnHandler,     false, 0, true);
negative_btn.addEventListener(  MouseEvent.CLICK, negative_btnHandler,   false, 0, true);


Note: You can even go beyond what I’ve done such as implementing a central function which encompasses all the filters. Then use an "if else if" or "switch" to execute the necessary code for the corresponding filter.

## Conclusion

There are many things you can do with filters, I’ve simply given a brief introduction. The basic filters in Flash are pretty good in that there are enough parameters to play around with. If you can’t find what you’re looking for, then try some of the other filters like ColorMatrixFilter, ConvolutionFilter, DisplacementMapFilter, etc. You may be surprised by what you find. Keep experimenting and most importantly, have fun!

Thank you for taking the time to view this tutorial. I hope you found it useful.

• http://flashden.net/user/msfx?ref=MSFX MSFX

Really nice tutorial on a subject with not many tutorials :)

Keep it up :) look forward to more :)

• cyber

sweet

• arrow23

Very handy man, nice one

• Steff

Very nice!!! Thx!

informative… thanks..

• http://laranzjoe.blogspot.com lawrence77

wow really nice, i love the outcome…
Soon i gonna give a try! ;)

• http://labs.dariux.com Dario Gutierrez

Good reference!! Thanks.

• http://www.frankyaguilar.com Franky

Genius! Thanks

• Pingback: Weekly Updates #7 « Powerusers

• http://www.dsaportfolio.com.br/ Diego SA

He-e-ey! What do we just dig out here? Matrix! Long time I didn’t see matrix! Looks like I’ll have to study matrix again… don’t remember right now! sorry!
But the final result is excellent.
And you call this a brief introduction? Holy mama! This is a wonderful introduction! A lot of congratulations for you!

• Michael Chen

Hi everyone,

Thank you for the feedback.

regards,
Mike

• qana6r-des.com

gooood

• elay

Hi really cool

I have a question is it difficult to modify this example to get a Effect like Before and after to compare not the Filter, i want to compare two different Pictures.

thanks