We’ll focus on the convolution, displacement map, and Perlin noise filters in this section, and then group a trio of color filters together in the following section.. Put simply, a convo
Trang 19 g beginFill (0xFFFF00, 1);
10 g drawRoundRect (0, 0, 200, 50, 20);
11 g endFill ();
12
13 sp filters = [ds];
14 addChild (sp);
Because we went the simple route of using a sprite for our interactive element
(rather than building a multistate button with the SimpleButton class, as seen
in the applied example at the end of Chapter 8), we need to set the buttonMode
property of the sprite to true in line 15 This won’t create the up, over, and
down states of a button symbol, but it will provide visual feedback by
chang-ing the cursor to the hand cursor when over the sprite
The listeners in lines 16 through 18 trigger functions based on mouse
behav-ior The mouse down listener function, onDown() (lines 20 to 22) removes the
drop shadow effect from the sprite by clearing the filters array Both the
mouse up and mouse out listeners point to the onUp() function in lines 23 to
25, which repopulates the filters array with the drop shadow This restores
the elevated “up” appearance to the sprite
15 sp buttonMode = true ;
16 sp addEventListener ( MouseEvent.MOUSE_DOWN , onDown, false , 0, true );
17 sp addEventListener ( MouseEvent.MOUSE_UP , onUp, false , 0, true );
18 sp addEventListener ( MouseEvent.MOUSE_OUT , onUp, false , 0, true );
19
20 function onDown(evt: MouseEvent ): void {
21 sp filters = [];
22 }
23 function onUp(evt: MouseEvent ): void {
24 sp filters = [ds];
25 }
Another way to handle this task would be to leave the ds filter active, but
change some of its properties For example, rather than eliminating the
shadow, you could reduce its distance value when the button is pressed
When the shadow appears closer to the object, the object’s virtual elevation
appears to be reduced
Using the BlurFilter to create an airbrush
With just a couple of lines of additional code, you can turn the brush from
the drawing tool developed previously in this chapter into an airbrush The
following ActionScript excerpt shows new code in bold, and can be found
in the paint_tool_erase_blur.fla source file The first new line (30) creates an
instance of the BlurFilter that blurs 40 pixels in the x and y direction The
second new line (39) applies the filter to the current tool Figure 9-10 shows
the result of softening the brush and eraser with these modifications
26 canvas addEventListener ( MouseEvent.MOUSE_DOWN ,
27 onDown, false , 0, true );
28 canvas addEventListener ( MouseEvent.MOUSE_UP , onUp,
29 false , 0, true );
30 canvas addEventListener ( Event.ENTER_FRAME , onLoop,
31 false , 0, true );
N OT E
For information about creating arrays with bracket syntax ([]), see Chapter 2.
N OT E
Filters can be used in creative ways
If you wanted to simulate casting a shadow from a moving light source, you could vary the distance, angle, and alpha values of the DropShadowFilter See the “Animating Filters” post at the companion website, http://www.
LearningActionScript3.com , for more information.
Trang 233 var blur: BlurFilter = new BlurFilter (40, 40);
34
35 function onDown(evt: MouseEvent ): void {
36 mouseIsDown = true ;
37 if (evt shiftKey ) {
38 tool = eraser;
39 } else {
40 tool = brush;
41 }
42 tool filters = [blur];
43 }
Advanced Filters
A number of more advanced ActionScript filters allow you to mimic some of the special effects features in pixel-editing applications like Photoshop We’ll focus on the convolution, displacement map, and Perlin noise filters in this section, and then group a trio of color filters together in the following section
Convolution filter
Convolution filtering is typically a part of many visual effects in most, if not all, pixel-editing applications Photoshop offers direct access to a convolution filter (renamed to Custom some years ago, and found in the Filters→Other
menu), but usually the filter works quietly behind the scenes
Put simply, a convolution filter calculates pixel color values by combining color values from adjacent pixels Combining these colors in different ways (using different values in the matrix) produces a wide variety of image effects These effects include, but are not limited to, blurring, sharpening, embossing, edge detection, and brightness
Using the filter effectively requires at least a working knowledge of matrices
so, if you haven’t read Chapter 8, do so now Although still a matrix, visualized
as a grid of numbers, the ConvolutionFilter doesn’t use the same matrix for-mat discussed in Chapter 8 Instead, you can define any number of rows and columns in a convolution matrix, and the structure of the matrix determines how each pixel is affected
Unless you plan to delve deeply into writing your own filters, you probably don’t need to learn the algorithms behind how a convolution matrix works
In most circumstances, you’ll use an existing matrix for a specific effect and use experimentation to determine a satisfactory setting
To give your experimenting some focus, let’s look at three parts of the matrix: the center grid element, the grid symmetry, and the sum of all grid elements Consider a 3 × 3 matrix The center value in the matrix represents the current pixel (all pixels in an image are analyzed), while the remaining elements are the eight adjacent pixels The numbers in each matrix position determine how the color values of that pixel affect the current pixel The basic idea is that each of the nine pixels is given a weight, or importance, that affects how they are altered
Figure 9-10. A Blur filter applied to
the drawing tool in the ongoing paint
application
Trang 3Blur Brightness Edges
Figure 9-11. Example convolution filter effects
A convolution matrix of all zeros will turn an image black because no color
values are used for any pixel, including the current pixel in the center of the
grid Using a 1 in the center of an all-zero grid won’t change the image because
the current pixel is unchanged (default value of 1), and no color values from
surrounding pixels are used Placing a 2 in the center of an all-zero grid will
brighten the image because no colors from surrounding pixels are used, but
the weight of the color values of the current pixel are increased
The ConvolutionFilter constructor appearing on line 8, 14, and 20 in the
following code example requires two parameters, the number of rows and
the number of columns A third, optional parameter is the matrix used to
affect the image If no matrix is furnished a default (no change) matrix is
used Applying a default matrix allows you to remove any changes made by
prior convolution filters, as seen in the event listener that follows This code
is found in the convolution_filter_basics.fla source file
1 var black: ConvolutionFilter ;
2 var noChange: ConvolutionFilter ;
3 var brightness: ConvolutionFilter ;
4
5 var blackArr: Array = [0, 0, 0,
6 0, 0, 0,
7 0, 0, 0];
8 black = new ConvolutionFilter (3, 3, blackArr);
9 mc0 filters = [black];
10
Trang 411 var noChangeArr: Array = [0, 0, 0,
12 0, 1, 0,
13 0, 0, 0];
14 noChange = new ConvolutionFilter (3, 3, noChangeArr);
15 mc1 filters = [noChange];
16
17 var brightnessArr: Array = [0, 0, 0,
18 0, 2, 0,
19 0, 0, 0];
20 brightness = new ConvolutionFilter (3, 3, brightnessArr);
21 mc2 filters = [brightness];
22
23 stage addEventListener ( MouseEvent.CLICK , onClick,
24 false , 0, true );
25
26 function onClick(evt: MouseEvent ): void {
27 for ( var i: int = 0; i < 3; i++) {
28 var mc: MovieClip = MovieClip ( getChildAt (i));
29 mc filters = [noChange];
30 }
31 } Now let’s focus on the symmetry of the surrounding grid elements The remainder of the code in this section is found in the convolution_filter_more fla source file, and demonstrates centralizing the filter creation into a
func-tion called convFilter() to reduce repeating code The funcfunc-tion appears at the end of the discussion so you can focus on the matrices we’re discussing The embossUp example uses reduced values for the three pixels to the upper left
of the current pixel, and increased values for the three pixels to the lower right
of the current pixel The result is a traditional embossing effect (see Figure 9-11)
By contrast, the embossDown example reverses this effect, seemingly stamping
into the image
32 var embossUp: Array = [-1, -1, 0,
33 -1, 1, 1,
34 0, 1, 1];
35 convFilter(mc0, embossUp);
36
37 var embossDown: Array = [1, 1, 0,
38 1, 1, -1,
39 0, -1, -1];
40 convFilter(mc1, embossDown);
As our third area of interest, we want to focus on the fact that overall image brightness is affected by the sum of all elements in the matrix In each of the prior examples, all the matrix elements add up to 1, except brighter (which
adds up to 2) and black (which adds up to 0) The following example is a
matrix that uses the left, top, right, and bottom adjacent pixel color values to affect the current pixel The result is a blurring effect However, a dramatic brightening of the image occurs because the sum of the matrix elements is 5, not 1 The affected image is five times brighter
If this is not desired, you can compensate by using an optional fourth param-eter of the ConvolutionFilter class, called a divisor The sum of the matrix will be divided by this value and the result will affect the brightness If the
Trang 5result is 1, the brightening effect of the matrix will be eliminated The first
filter instance here uses only the first three parameters without compensating
for brightness The second instance adds the divisor as the fourth parameter,
bringing the brightness back to the original state, leaving only the blur effect
41 var blurBright: Array = [0, 1, 0,
42 1, 1, 1,
43 0, 1, 0];
44 convFilter(mc2, blurBright);
45
46 var blurOnly: Array = [0, 1, 0,
47 1, 1, 1,
48 0, 1, 0];
49 convFilter(mc3, blurOnly, 5);
As a summary of what we’ve learned, we’ll look at how sharpen and find
edges filters differ The sharpen instance that follows uses negative values for
the left, top, right, and bottom pixels, which is the opposite of blur and causes
the pixels to pop The sum of the matrix is 1, meaning there is no increase or
decrease in brightness
The edges instance uses the same values for the surrounding pixels, but the
sum of the array is 0 This has a sharpening effect but reduces the brightness,
leaving only the emphasized edges visible
50 var sharpen: Array = [ 0, -1, 0,
51 -1, 5, -1,
52 0, -1, 0];
53 convFilter(mc4, sharpen);
54
55 var edges: Array = [ 0, -1, 0,
56 -1, 4, -1,
57 0, -1, 0];
58 convFilter(mc5, edges);
The function that applies these matrices differs from the prior source file in
only one major respect: It provides for the use of the fourth optional
param-eter, divisor, to compensate for accumulated brightness.
59 function convFilter(dispObj: DisplayObject , matrix: Array ,
60 divisor: int =1): void {
61 var conv: ConvolutionFilter =
62 new ConvolutionFilter (3, 3, matrix, divisor);
63 dispObj filters = [conv];
64 }
Perlin noise and displacement map
Two other very useful and entertaining effects supported by ActionScript
are the Perlin noise generator and the displacement map filter Perlin noise
is widely used for generating naturalistic animated effects like fog, clouds,
smoke, water, and fire, as well as textures like wood, stone, and terrain
Displacement maps are used to translate (or displace) pixels to add extra
dimension to surfaces They are commonly used to add realism to textures
(such as a pitted or grooved surface) as well as distort images to appear as if
seen through a refracting material like glass or water
N OT E
Ken Perlin developed the Perlin noise algorithm while creating the special effects for the 1982 film Tron At the time, the extensive use of effects in that film may have been cost-prohibitive using traditional multi-exposure film compositing techniques Perlin noise was used to manipulate the near-constant computer-generated glows and shad-ings, among other effects Mr Perlin won an Academy Award for Technical Achievement in 1997 for his contribu-tions to the industry.
Trang 6The following exercise, found in the perlin_displacement.fla source file, will
cre-ate an animcre-ated Perlin noise texture that will then be used as the source for a displacement map In our example, the Perlin noise will include random areas
of blue, as shown in Figure 9-12 These blue areas will cause the displacement
in a photo of a reef aquarium, and the combined effect will cause soft corals in the scene to undulate as if experiencing the effect of water currents
The source material we’ll use is a picture of a reef aquarium, as seen in Figure 9-13 The sea life are the sole elements in a foreground image, and will be affected by the filters so that they will appear to be moving in the water cur-rent The rock is in a separate background and will not be affected
Perlin noise
The first step in building our aquarium simulation is to create a BitmapData object to contain the Perlin noise Our image will cover the stage, so we’ll pass the stage width and height into the object to size it appropriately (line 1) Lines 3 and 4 create a bitmap using the bitmap data, and then add that bitmap to the display list However, the lines are commented out because we
do not want to see the Perlin noise in the final product We need access only
to the bitmap data to drive the displacement map However, it’s often help-ful to see the Perlin noise as you work so you can experiment with various settings By uncommenting these lines, you can adjust the Perlin noise values until you’re satisfied with the effect, and then comment out these lines again when moving on to the displacement filter
1 var bmpData: BitmapData = new BitmapData ( stage.stageWidth ,
2 stage.stageHeight );
3 //var bmp:Bitmap = new Bitmap(bmpData);
4 //addChild(bmp);
5 //comment out lines 3 and 4 to see Perlin noise
The Perlin noise generator has a number of settings that will produce dra-matically different results when adjusted As we discuss these settings, we’ll reference natural phenomena, like water and smoke We’ll first discuss the settings of the filter and then simply pass these settings into the perlin-Noise() method later on in lines 30 through 32.
Lines 7 and 8 set the scale of the texture in the x and y directions Think of this as influencing the number of waves you can see at one time in water A very large scale might result in the look of a swelling sea, and a small scale might look like a babbling brook
Line 7 determines the number of octaves in the texture, which are discreet layers of noise that function independently of each other A single-octave noise will not be as complex as a multi-octave noise and, during animation, you can move a single-octave noise in only one direction at a time You can create basic animations with single octaves, like the look of running water
or, in our case, underwater current But the ability to move each octave in a different direction makes multi-octave noise better suited for effects like col-liding waves moving in multiple directions, or fire, or smoke
Figure 9-12. Perlin noise texture
Figure 9-13. Elements of the Perlin noise
and displacement map filter exercise
Trang 7Line 10 creates a random seed to influence the starting point for the creation
of the texture A random seed allows you to randomize the effect but also call
back that same result by using the same seed at a later time In our case, we
only care about the randomization, so we’ll use a random number, between
0 and 100, for the seed as well
6 //perlin noise settings
7 var baseX: Number = 50;
8 var baseY: Number = 50;
9 var numOctaves: Number = 1;
10 var randomSeed: Number = Math.random () * 100;
11 var stitch: Boolean = true ;
12 var fractalNoise: Boolean = true ;
13 var channelOptions: Number = BitmapDataChannel.BLUE ;
14 var grayScale: Boolean = false ;
15 var offsets: Array = new Array ( new Point ());
Line 11 determines whether the edges of the area defined when creating the
noise pattern are stitched together in an attempt to create a seamless tile
When creating static textures, this “stitching” is not usually needed, but it’s
recommended when animating the effect
Whether fractal noise or turbulence techniques are used when generating
the effect is determined by line 12 Fractal noise (used when the fractalNoise
property is true) generates a smoother effect; turbulence (used when the
fractalNoise property is false) produces more distinct transitions between
levels of detail For example, fractal noise might be used to create a terrain
map of rolling hills or an oceanscape, and turbulence might be better suited
to a terrain of mountains or crevices in a rock
Line 13 chooses which channels of bitmap data are used when generating
the texture: red, green, blue, and/or alpha These can be indicated by
con-stants from the BitmapDataChannel class or with integers You can also use a
special operator called the bitwise OR operator (|) to combine channels to
create multicolor effects or combine color with alpha For example,
combin-ing alpha with noise can create fog or smoke with transparent areas through
which a background can be seen
In this exercise, because we are generating a pattern only to provide data for
a displacement map, we need only one channel (Blue was chosen arbitrarily.)
However, experimenting with the Perlin noise settings can make it difficult
to visualize the texture’s effect To improve these efforts a bit, you can add
alpha data to the mix, so you can see the underlying image through the
pat-tern Figure 9-14 shows the visible noise texture and the reef beneath it In our
finished example, the anemones will be displaced to a greater degree where
blue is more visible
To see the background image as you experiment with the noise settings, you
just have to add an alpha channel to the channelOptions property To do this,
replace line 13 with this:
13 var channelOptions: Number = BitmapDataChannel.BLUE |
BitmapDataChannel.ALPHA ;
N OT E
Perlin noise layers are called octaves because, like musical octaves, each one doubles the frequency of the previous octave, increasing detail within the tex-ture It’s also important to note that the processor power required to generate noise patterns increases with the num-ber of octaves used.
Figure 9-14. Perlin noise detail without alpha data
Trang 8The grayscale parameter in line 14 desaturates the texture so it generates
only grays In our exercise, the texture won’t be visible, so this isn’t relevant, but it’s ideal when visible fog or smoke is required
Finally, line 15 uses an array of offset points, one for each octave, to control the location of the noise pattern generated We need only one octave in this example, so this is a single-item array Because the texture will not be visible, its starting point is arbitrary, so we’ll use a default point of (0, 0) During animation, we’ll alter the position of this point to move the pattern
You’ve now set all the values required to create a Perlin noise texture If you want to see the noise before moving on to the next section, look at the per-lin_noise_only.fla source file Later, we’ll animate these values by changing the
offset values upon every enter frame event First, however, we need to set up the displacement map settings
Displacement map
The displacement map filter is a bit simpler Lines 18 and 19 of the script that follows determine which color channel will affect the distortion in each direction We used the blue channel when creating our Perlin noise texture,
so we’ll use the same channel here
Next, lines 20 and 21 set the scale of displacement in the x and y directions Think of these values as the size of the waves when looking through water, or the degree of refraction when looking through glass, in each direction
16 //displacement map settings
17 var displaceMap: DisplacementMapFilter ;
18 var componentX: uint = BitmapDataChannel.BLUE ;
19 var componentY: uint = BitmapDataChannel.BLUE ;
20 var xScale: Number = 10;
21 var yScale: Number = 10;
22 displaceMap = new DisplacementMapFilter (bmpData, new Point (),
23 componentX, componentY, xScale, yScale,
24 DisplacementMapFilterMode.CLAMP );
Finally, lines 22 through 23 determine how the edges of the displacement map will behave When set to clamp, any displacement will be confined by
the edges of the source data If wrap, is used, the distortion will wrap around
from edge to edge The wrap option is great for tiled patterns but not useful
for affecting a realistic image of a recognizable object You don’t want to see the top of a person’s head appearing beneath their feet as a displacement wraps from top to bottom edge
Now that our settings are complete, we create the DisplacementMapFilter
in lines 22 through 24 The source for the displacement data is the same
BitmapData object that is being affected by the Perlin noise pattern, so the
degree of displacement will be determined by that bitmap data, passed into the class in the first parameter The second parameter is the map point—the location at which the upper-left corner of the displacement map filter will
be applied This is useful for filtering only a portion of the image We want
Trang 9to filter the entire image, however, so we’ll pass in a default point to begin
filtering at (0, 0) The remainder of the parameters correspond directly to the
settings previously created
Animating the effect
To animate the Perlin noise, and therefore the displacement map effect, we
start with a listener that triggers the onLoop() function upon every enter
frame event The first thing the function does is update the offset point for
the Perlin noise octave, seen in lines 28 and 29 This example sets the offset
point of the octave, not the location of a display object (see the adjacent note
for more information) Lines 28 and 29 move the first octave (the only octave
used in our example) up and to the right, 2 pixels in each direction
With each change to the offset point, the perlinNoise() method is called
(line 30), applying all the previously set parameters along with the offset
update Finally, with the call of the Perlin noise method, the DisplacementMap
filter source data is updated, so the DisplacementMap filter must be reapplied
to the display object in line 33
25 //enter frame update of both filters
26 addEventListener ( Event.ENTER_FRAME , onLoop, false , 0, true );
27 function onLoop(evt: Event ): void {
28 offsets[0] x -= 2;
29 offsets[0] y += 2;
30 bmpData perlinNoise (baseX, baseY, numOctaves, randomSeed,
31 stitch, fractalNoise, channelOptions,
32 grayScale, offsets);
33 tank_mc filters = [displaceMap];
34 }
Color Effects
You can apply color effects using ActionScript in multiple ways, and we’ll
look at three The first is relatively straightforward: Alter the emphasis of
individual color channels (red, green, blue, and alpha) in an image using the
ColorTransform class The second is a more powerful technique that uses the
ColorMatrixFilter to apply a detailed matrix to simultaneously change all
color and alpha channels The last method discussed is the simplest, using
the Color class to apply a tint to a display object
The ColorTransform Class
Although we’ll focus exclusively on color, you can use the ColorTransform
class to adjust the alpha channel as well as the individual color channels of
a display object or BitmapData object In the examples that follow, we’ll be
using the class to invert an image (create a color negative) and apply a simple
saturation effect
N OT E
Animating an octave with the offset property is not the same as moving a display object Instead, think of adjust-ing the offset position of an octave as adjusting that octave’s registration point
If you move a movie clip five pixels
in the x and y directions, it will move down and to the right However, if you adjust the movie clip’s registration point, down and to the right, the clip won’t move on stage, but its contents will move up and to the left
Trang 10The class offers two ways to change color First, you can multiply a color chan-nel to increase or decrease its effect For example, you can double the weight of the color by using a multiplier of 2, and you can reduce the weight of the color
by half by using 0.5 as a multiplier Second, you can offset a color channel from –255 to 255 For example, assuming a default multiplier of 1 (no change from the multiplier), an offset value of 255 would maximize the red channel, 0 would apply no change, and –255 would remove all red from the image
The following code, found in the color_transform.fla source file, manipulates
three movie clips instantiated as mc0, mc1, and mc2 Lines 1 through 10 show the default ColorTransform instance, which makes no change to the source material Also, by using this default configuration (with a multiplier of 1 and an offset of 0 on each channel), you can effectively reset any prior color transformation Despite the example’s focus on color, we’ve included alpha multiplier and offset values to show the complete syntax of a reset
Note that the ColorTransform class is not a filter, so it’s not applied to the
fil-ters property of a display object Instead, the color transformation is applied
to the colorTransform property of a display object’s transform object (line
10) Similar to the filtering process, however, every time a change is made to the color transformation, it must be reapplied to the colorTransform property Lines 12 through 19 provide for an increase in saturation The offset values
of all colors are unchanged, but the color multipliers increase the color for each channel To emphasize the image, the values used increase red more than green and blue This effect can be seen in the “Saturation” example in Figure 9-15 You could also partially desaturate an image using the same technique but applying a multiplier value of less than 1 to each color channel
Finally, lines 21 through 28 invert all color in the image The multiplier for all color channels is set to –1, which effectively turns the image black, and then the offset values are set to full to revert back to color This effect can be seen
in the “Invert” example from Figure 9-15
1 var noChange: ColorTransform = new ColorTransform ();
2 noChange redOffset = 0;
3 noChange greenOffset = 0;
4 noChange blueOffset = 0;
5 noChange alphaOffset = 0;
6 noChange redMultiplier = 1;
7 noChange greenMultiplier = 1;
8 noChange blueMultiplier = 1;
9 noChange alphaMultiplier = 1;
10 mc0 transform.colorTransform = noChange;
11
12 var saturation: ColorTransform = new ColorTransform ();
13 saturation redOffset = 0;
14 saturation greenOffset = 0;
15 saturation blueOffset = 0;
16 saturation redMultiplier = 1.3;
17 saturation greenMultiplier = 1.1;
18 saturation blueMultiplier = 1.1;
19 mc1.tr ansform.colorTransform = saturation;
20
Original
Saturation
Invert
Figure 9-15. ColorTransform filter effects