• home
  • forum
  • my
  • kt
  • download
  • Comparing Filters in Flash

    Author: 2009-07-10 02:41:46 From:

    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.

    view plaincopy to clipboardprint?
    1. // Applying a blur filter to a DisplayObject.   
    2. var bFilter:BlurFilter = new BlurFilter();   
    3. var arrFilters:Array = [bFilter];   
    4. 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.

    view plaincopy to clipboardprint?
    1. // Default   
    2. var matrixDefault:Array = [0, 0, 0,   
    3.                            0, 1, 0,   
    4.                            0, 0, 0];    
    5.   
    6. // Brighter   
    7. var matrixBrighter:Array = [0, 0, 0,   
    8.                             0, 2, 0,   
    9.                             0, 0, 0];   
    10.   
    11. // Darker   
    12. var matrixDarker:Array = [0, 0,   0,   
    13.                           0, 0.5, 0,   
    14.                           0, 0,   0];   
    15.   
    16. // Emboss   
    17. var matrixEmboss:Array = [-1, -1, 0,   
    18.                           -1,  1, 1,   
    19.                            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:

    view plaincopy to clipboardprint?
    1. // The following matrix will brighten the image.   
    2. var matrixBrighter:Array = [0, 0, 0,   
    3.                             0, 2, 0,   
    4.                             0, 0, 0];   
    5. var matrixCol:Number = 3;   // Number of column   
    6. var matrixRow:Number = 3;   // Number of row   
    7. var cvFilter:ConvolutionFilter = new ConvolutionFilter(matrixCol, matrixRow, matrixBrighter);   
    8. var arrFilters:Array = [cvFilter];   
    9. displayObj.filters = arrFilters;  

    For more information about Convolution filter, refer to this Adobe Flash Article.

    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.

    view plaincopy to clipboardprint?
    1. import fl.managers.StyleManager;   
    2. var format:TextFormat = new TextFormat("Verdana", 10, 0x000000);   
    3. 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".

    view plaincopy to clipboardprint?
    1. /*  
    2. // Version A  
    3. var photoEffected:PhotoDandelion = new PhotoDandelion();  
    4. photoEffected.x = photoOriginal.x;  
    5. photoEffected.y = photoOriginal.y;  
    6. addChild(photoEffected);  
    7. */  
    8.   
    9. // Version B   
    10. var photoWrapper:Sprite = new Sprite();   
    11. photoWrapper.x = photoOriginal.x;   
    12. photoWrapper.y = photoOriginal.y;   
    13. var photoEffected:PhotoDandelion = new PhotoDandelion();   
    14. photoWrapper.addChild(photoEffected);   
    15. addChild(photoWrapper);  

    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.

    view plaincopy to clipboardprint?
    1. var comparisonLine:Shape = new Shape();   
    2. comparisonLine.x = photoOriginal.x + photoOriginal.width/2;   
    3. comparisonLine.y = photoOriginal.y + photoOriginal.height;   
    4. comparisonLine.graphics.lineStyle(2, 0x999999, 1, true);   
    5. comparisonLine.graphics.moveTo(0, 0);   
    6. comparisonLine.graphics.lineTo(0, -photoOriginal.height);   
    7. addChild(comparisonLine);  

    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.

    1. mask_sld.x = photoOriginal.x;   
    2. mask_sld.y = photoOriginal.y + photoOriginal.height;   
    3. mask_sld.minimum = 0;   
    4. mask_sld.maximum = photoOriginal.width;   
    5. mask_sld.value   = photoOriginal.width/2;   
    6. mask_sld.width   = photoOriginal.width;  

    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.

    view plaincopy to clipboardprint?
    1. import fl.events.SliderEvent;   
    2.   
    3. function maskSliderDrag(evt:SliderEvent):void {   
    4.     comparisonLine.x = evt.target.value + evt.target.x;   
    5.     photoMask.width  = evt.target.value;   
    6. }   
    7.   
    8. mask_sld.addEventListener(SliderEvent.THUMB_DRAG, maskSliderDrag, false, 0, true);  

    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".

    view plaincopy to clipboardprint?
    1. var photoMask:Shape = new Shape();   
    2. photoMask.x = photoOriginal.x;   
    3. photoMask.y = photoOriginal.y;   
    4. photoMask.graphics.beginFill(0xff9900);   
    5. photoMask.graphics.drawRect(0, 0, photoOriginal.width, photoOriginal.height);   
    6. photoMask.graphics.endFill();   
    7. addChild(photoMask);   
    8.   
    9. photoWrapper.mask = photoMask;   
    10. photoMask.width = mask_sld.value;  

    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.

    view plaincopy to clipboardprint?
    1. 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.

    view plaincopy to clipboardprint?
    1. function intensityChange(evt:SliderEvent):void  {   
    2.     activeFilter.dispatchEvent(new MouseEvent(MouseEvent.CLICK));   
    3. }   
    4. intensity_sld.addEventListener(SliderEvent.THUMB_DRAG, intensityChange, false, 0, true);    
    5. 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.

    view plaincopy to clipboardprint?
    1. function default_btnHandler(evt:MouseEvent):void  {   
    2.     var matrixDefault:Array = [0, 0, 0,   
    3.                                0, 1, 0,   
    4.                                0, 0, 0];   
    5.        
    6.     var conv:ConvolutionFilter = new ConvolutionFilter(3, 3 , matrixDefault);   
    7.     photoEffected.filters = [conv];   
    8.        
    9.     activeFilter = evt.target as Button;   
    10.     intensity_sld.enabled = false;   
    11. }   
    12.   
    13. function blur_btnHandler(evt:MouseEvent):void  {   
    14.     if(activeFilter != evt.target)  {   
    15.         intensity_sld.maximum = 30;   
    16.         intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;       
    17.         intensity_sld.value = intensity_sld.maximum/2;   
    18.     }   
    19.   
    20.     var blurX:Number = intensity_sld.value;   
    21.     var blurY:Number = intensity_sld.value;   
    22.     photoEffected.filters = [new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH)];   
    23.        
    24.     activeFilter = evt.target as Button;   
    25.     intensity_sld.enabled = true;   
    26. }   
    27.   
    28. function emboss_btnHandler(evt:MouseEvent):void  {   
    29.     if(activeFilter != evt.target)  {   
    30.         intensity_sld.maximum = 4;   
    31.         intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;       
    32.         intensity_sld.value = intensity_sld.maximum/2;   
    33.     }      
    34.        
    35.     var matrixEmboss:Array = [-intensity_sld.value, -intensity_sld.value, 0,   
    36.                               -intensity_sld.value, 1, intensity_sld.value,   
    37.                                0, intensity_sld.value, intensity_sld.value];   
    38.        
    39.     var conv:ConvolutionFilter = new ConvolutionFilter(3, 3, matrixEmboss);   
    40.     photoEffected.filters = [conv];   
    41.        
    42.     activeFilter = evt.target as Button;   
    43.     intensity_sld.enabled = true;      
    44. }   
    45.   
    46. function brightness_btnHandler(evt:MouseEvent):void  {   
    47.     if(activeFilter != evt.target)  {      
    48.         intensity_sld.maximum = 6;   
    49.         intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;   
    50.         intensity_sld.value = intensity_sld.maximum/2;   
    51.     }   
    52.   
    53.     var matrixBrightness:Array = [0, 0, 0,   
    54.                                   0, intensity_sld.value, 0,   
    55.                                   0, 0, 0];   
    56.   
    57.     var conv:ConvolutionFilter = new ConvolutionFilter(3, 3, matrixBrightness);   
    58.     photoEffected.filters = [conv];    
    59.        
    60.     activeFilter = evt.target as Button;   
    61.     intensity_sld.enabled = true;   
    62. }   
    63.   
    64. function negative_btnHandler(evt:MouseEvent):void  {   
    65.     var matrixNegative:ColorMatrixFilter = new ColorMatrixFilter([-1,  0,  0, 0, 255,   
    66.                                                                    0, -1,  0, 0, 255,   
    67.                                                                    0,  0, -1, 0, 255,   
    68.                                                                    0,  0,  0, 1,   0]);   
    69.     photoEffected.filters = [matrixNegative];   
    70.   
    71.     activeFilter = evt.target as Button;   
    72.     intensity_sld.enabled = false;   
    73. }  

    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.

    view plaincopy to clipboardprint?
    1. default_btn.addEventListener(   MouseEvent.CLICK, default_btnHandler,    false, 0, true);   
    2. blur_btn.addEventListener(      MouseEvent.CLICK, blur_btnHandler,       false, 0, true);   
    3. emboss_btn.addEventListener(    MouseEvent.CLICK, emboss_btnHandler,     false, 0, true);   
    4. brightness_btn.addEventListener(MouseEvent.CLICK, brightness_btnHandler, false, 0, true);   
    5. 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...

    view plaincopy to clipboardprint?
    1. import fl.events.SliderEvent;   
    2. import fl.managers.StyleManager;   
    3.   
    4.   
    5. var format:TextFormat = new TextFormat("Verdana", 10, 0x000000);   
    6. StyleManager.setStyle("textFormat", format);   
    7.   
    8.   
    9. var photoWrapper:Sprite = new Sprite();   
    10. photoWrapper.x = photoOriginal.x;   
    11. photoWrapper.y = photoOriginal.y;   
    12. var photoEffected:PhotoDandelion = new PhotoDandelion();   
    13. photoWrapper.addChild(photoEffected);   
    14. addChild(photoWrapper);   
    15.   
    16.   
    17. var comparisonLine:Shape = new Shape();   
    18. comparisonLine.x = photoOriginal.x + photoOriginal.width/2;   
    19. comparisonLine.y = photoOriginal.y + photoOriginal.height;   
    20. comparisonLine.graphics.lineStyle(2, 0x999999, 1, true);   
    21. comparisonLine.graphics.moveTo(0, 0);   
    22. comparisonLine.graphics.lineTo(0, -photoOriginal.height);   
    23. addChild(comparisonLine);   
    24.   
    25.   
    26. mask_sld.x = photoOriginal.x;   
    27. mask_sld.y = photoOriginal.y + photoOriginal.height;   
    28. mask_sld.minimum = 0;   
    29. mask_sld.maximum = photoOriginal.width;   
    30. mask_sld.value   = photoOriginal.width/2;   
    31. mask_sld.width   = photoOriginal.width;   
    32.   
    33.   
    34. function maskSliderDrag(evt:SliderEvent):void {   
    35.     comparisonLine.x = evt.target.value + evt.target.x;   
    36.     photoMask.width  = evt.target.value;   
    37. }   
    38. mask_sld.addEventListener(SliderEvent.THUMB_DRAG, maskSliderDrag);   
    39.   
    40.   
    41. var photoMask:Shape = new Shape();   
    42. photoMask.x = photoOriginal.x;   
    43. photoMask.y = photoOriginal.y;   
    44. photoMask.graphics.beginFill(0xff9900);   
    45. photoMask.graphics.drawRect(0, 0, photoOriginal.width, photoOriginal.height);   
    46. photoMask.graphics.endFill();   
    47. addChild(photoMask);   
    48.   
    49. photoWrapper.mask = photoMask;   
    50. photoMask.width = mask_sld.value;   
    51.   
    52.   
    53. var activeFilter:Button;   
    54.   
    55.   
    56. function intensityChange(evt:SliderEvent):void  {   
    57.     activeFilter.dispatchEvent(new MouseEvent(MouseEvent.CLICK));   
    58. }   
    59. intensity_sld.addEventListener(SliderEvent.THUMB_DRAG, intensityChange);   
    60. intensity_sld.enabled = false;   
    61.   
    62.   
    63. function applyConvolutionFilter(matrix:Array):void  {   
    64.     var cFilter:ConvolutionFilter = new ConvolutionFilter(3, 3 , matrix);   
    65.     photoEffected.filters = [cFilter];   
    66. }   
    67. function activeFilterAndIntensityOnOff(btn:Button, intensityOnOff:Boolean):void  {   
    68.     activeFilter = btn;   
    69.     intensity_sld.enabled = intensityOnOff;   
    70. }   
    71. function setSliderProperties(btn:Button, max:Number):void  {   
    72.     if(activeFilter != btn)  {   
    73.         intensity_sld.maximum = max;   
    74.         intensity_sld.snapInterval = (intensity_sld.maximum - intensity_sld.minimum)/40;       
    75.         intensity_sld.value = intensity_sld.maximum/2;   
    76.     }   
    77. }   
    78.   
    79.   
    80. function default_btnHandler(evt:MouseEvent):void  {   
    81.     var matrixDefault:Array = [0, 0, 0,   
    82.                                0, 1, 0,   
    83.                                0, 0, 0];   
    84.     applyConvolutionFilter(matrixDefault);   
    85.     activeFilterAndIntensityOnOff(evt.target as Button, false);   
    86. }   
    87.   
    88. function blur_btnHandler(evt:MouseEvent):void  {   
    89.     var btn:Button = evt.target as Button;   
    90.     setSliderProperties(btn, 30);   
    91.     var blurX:Number = intensity_sld.value;   
    92.     var blurY:Number = intensity_sld.value;   
    93.     photoEffected.filters = [new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH)];   
    94.     activeFilterAndIntensityOnOff(btn, true);   
    95. }   
    96.   
    97. function emboss_btnHandler(evt:MouseEvent):void  {   
    98.     var btn:Button = evt.target as Button;   
    99.     setSliderProperties(btn, 4);       
    100.     var matrixEmboss:Array = [-intensity_sld.value, -intensity_sld.value, 0,   
    101.                               -intensity_sld.value, 1, intensity_sld.value,   
    102.                                0, intensity_sld.value, intensity_sld.value];   
    103.     applyConvolutionFilter(matrixEmboss);   
    104.     activeFilterAndIntensityOnOff(btn, true);   
    105. }   
    106.   
    107. function brightness_btnHandler(evt:MouseEvent):void  {   
    108.     var btn:Button = evt.target as Button;   
    109.     setSliderProperties(btn, 6);   
    110.     var matrixBrightness:Array = [0, 0, 0,   
    111.                                   0, intensity_sld.value, 0,   
    112.                                   0, 0, 0];   
    113.     applyConvolutionFilter(matrixBrightness);      
    114.     activeFilterAndIntensityOnOff(btn, true);   
    115. }   
    116.   
    117. function negative_btnHandler(evt:MouseEvent):void  {   
    118.     var matrixNegative:ColorMatrixFilter = new ColorMatrixFilter([-1,  0,  0, 0, 255,   
    119.                                                                    0, -1,  0, 0, 255,   
    120.                                                                    0,  0, -1, 0, 255,   
    121.                                                                    0,  0,  0, 1,   0]);   
    122.     photoEffected.filters = [matrixNegative];   
    123.     activeFilterAndIntensityOnOff(evt.target as Button, false);   
    124. }   
    125.   
    126.   
    127. default_btn.addEventListener(   MouseEvent.CLICK, default_btnHandler,    false, 0, true);     
    128. blur_btn.addEventListener(      MouseEvent.CLICK, blur_btnHandler,       false, 0, true);     
    129. emboss_btn.addEventListener(    MouseEvent.CLICK, emboss_btnHandler,     false, 0, true);     
    130. brightness_btn.addEventListener(MouseEvent.CLICK, brightness_btnHandler, false, 0, true);     
    131. 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.

    discuss this topic to forum

    relation tutorial

    No information

    Category

      3D (36)
      Math Physics (18)
      3rd Party (10)
      Navigation (70)
      Actionscripting (228)
      Optimization (17)
      Animation (166)
      Projector (11)
      Audio (54)
      Special Effects (170)
      Backend (26)
      Text Effects (89)
      Drawing (34)
      Tips and Techniques (57)
      Dynamic Content (38)
      Tricks (8)
      Games (114)
      Utilities (24)
      Getting Started (99)
      Video (59)
      Interactivity (48)
      Web Design (37)

    New

    Hot