topical media & game development
#graphic-flex-image-effects-02-Flex-BlendModes.ax
#graphic-flex-image-effects-02-Flex-BlendModes.ax
[swf]
[flash]
flex
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.utils.ByteArray;
import flash.utils.describeType;
[SWF(width=900, height=550, backgroundColor=0x333333)]
Class to test the application of blend modes. Two images are loaded and displayed, then copied and stacked
on the right of the stage. Clicking on the field with the blend mode name changes the blend mode. The colors
beneath the mouse in all three images (two loaded and the stacked composite) are displayed below the images.
Doubleclicking an image allows you to load in a new image in that position.
public class @ax-graphic-flex-image-effects-02-Flex-BlendModes extends Sprite {
// update this if the assets do not remain in the relative directory
private static const ASSETS_DIRECTORY:String = "graphic-flex-image-effects-02-assets-";
// update this if another shader file is used
private static const SHADER:String = ASSETS_DIRECTORY + "luminosity.pbj";
// update these to change the default images
private static const IMAGE_0:String = ASSETS_DIRECTORY + "goat.jpg";
private static const IMAGE_1:String = ASSETS_DIRECTORY + "stagecoach.jpg";
private static const IMAGE_WIDTH:uint = 300;
private static const IMAGE_HEIGHT:uint = 300;
private static const COLOR_RECT_HEIGHT:uint = 50;
private var _bitmapHolder:Sprite;
private var _bitmap0:Bitmap;
private var _bitmap1:Bitmap;
private var _bitmap2:Bitmap;
private var _bitmapLoading:Bitmap;
private var _blendedBitmap:Bitmap;
private var _colorRect:Shape;
private var _colorLabel0:TextField;
private var _colorLabel1:TextField;
private var _colorLabel2:TextField;
private var _targets:Shape;
private var _mouseDown:Boolean;
private var _blendModes:Array;
private var _blendModeLabel:TextField;
private var _file:FileReference;
private var _colorPoint:Point;
private var _shader:Shader;
Constructor. Simply calls init().
public function @ax-graphic-flex-image-effects-02-Flex-BlendModes() {
init();
}
Initializes bitmap holders, stored an arrat of all blend modes and begins load of first image.
private function init():void {
_bitmapHolder = new Sprite();
_bitmapHolder.doubleClickEnabled = true;
addChild(_bitmapHolder);
_blendModes = [];
// finds all the blend modes available to this sprite using describeType() and E4x
// to grab the constant node from the XML that describeType returns for BlendMode
var blendModes:XMLList = describeType(BlendMode).constant;
// runs through all of the constants, which are all the names of the blend modes
for each (var blendMode:XML in blendModes) {
// pushes each constant name into the array
_blendModes.push(blendMode.@name.toString());
}
loadImage(IMAGE_0);
}
Loads an image.
parameter: imagePath The path to the image to load.
private function loadImage(imagePath:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.load(new URLRequest(imagePath));
}
Loads the shader specified in the class constant.
private function loadShader():void {
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onShaderLoaded);
loader.load(new URLRequest(SHADER));
}
Adds an empty shape to the display list at the specified coordinate.
parameter: x The x position for the shape.
parameter: y The y position for the shape.
returns: The shape created.
private function addShape(x:Number, y:Number):Shape {
var shape:Shape = new Shape();
shape.x = x;
shape.y = y;
addChild(shape);
return shape;
}
Adds a shape in which the colors of the hovered over pixels will be drawn as rectangles
as well as a shape in which the targets showing the mouse position in each image can be drawn.
private function addColorShapes():void {
_colorRect = addShape(0, IMAGE_HEIGHT);
_targets = addShape(0, 0);
}
Adds a textfield with the specified TextFormat at the specified x/y position.
parameter: format The TextFormat to apply to the field.
parameter: x The x position for the field.
parameter: y The y position for the field.
returns: The field created.
private function addTextField(format:TextFormat, x:Number, y:Number):TextField {
var textField:TextField = new TextField();
textField.width = width;
textField.multiline = true;
textField.defaultTextFormat = format;
textField.x = x;
textField.y = y;
addChild(textField);
return textField;
}
Adds three textfields to display the hex colors from each image based on mouse position.
private function addColorLabels():void {
var x:Number = 5;
var y:Number = IMAGE_HEIGHT + COLOR_RECT_HEIGHT;
var width:Number = IMAGE_WIDTH;
var format:TextFormat = new TextFormat("Arial", 20, 0xFFFFFF);
// uses a tab stop in the format to better align the color values
format.tabStops = [50];
// place three fields, one for each image
_colorLabel0 = addTextField(format, x, y);
x += width;
_colorLabel1 = addTextField(format, x, y);
x += width;
_colorLabel2 = addTextField(format, x, y);
}
Adds a clickable textfield that will display the current blend mode.
private function addBlendModeLabel():void {
var labelHeight:uint = 100;
var format:TextFormat = new TextFormat("Arial", labelHeight);
format.align = TextFormatAlign.CENTER;
_blendModeLabel = new TextField();
_blendModeLabel.selectable = false;
_blendModeLabel.border = true;
_blendModeLabel.background = true;
_blendModeLabel.width = stage.stageWidth;
_blendModeLabel.defaultTextFormat = format;
_blendModeLabel.y = stage.stageHeight - labelHeight;
// display initial blend mode
_blendModeLabel.text = _blendModes[0];
// enable label to be clicked in order for blend mode to change
_blendModeLabel.addEventListener(MouseEvent.CLICK, onBlendModeClick);
addChild(_blendModeLabel);
}
Enables mouse events on the bitmap holder.
private function enableMouseEvents():void {
_bitmapHolder.addEventListener(MouseEvent.DOUBLE_CLICK, onHolderDoubleClick);
_bitmapHolder.addEventListener(MouseEvent.MOUSE_MOVE, onHolderMouseMove);
_bitmapHolder.addEventListener(MouseEvent.MOUSE_DOWN, onHolderMouseDown);
_bitmapHolder.addEventListener(MouseEvent.MOUSE_UP, onHolderMouseUp);
}
Returns a string containing the hex value and the individual rgb values of a decimal number.
parameter: color The decimal number representing a color.
returns: A string containing the hex and rgb values.
private function formatColor(color:uint):String {
var colorString:String = color.toString(16).toUpperCase();
// make sure we have 6 digits by adding 0's to the left
while (colorString.length < 6) {
colorString = "0" + colorString;
}
colorString = "hex:\t#" + colorString + "\n";
var red:uint = color >> 16 & 0xFF;
var green:uint = color >> 8 & 0xFF;
var blue:uint = color & 0xFF;
colorString += "r:\t" + red + "\n";
colorString += "g:\t" + green + "\n";
colorString += "b:\t" + blue;
return colorString;
}
Draws a colored rectangle at the specified position.
parameter: color The color to use as a fill.
parameter: x The x position for the rectangle.
parameter: y The y position for the rectangle.
parameter: width The width of the rectangle.
parameter: height The height of the rectangle.
private function drawColorRect(color:uint, x:uint, y:uint, width:uint, height:uint):void {
_colorRect.graphics.beginFill(color);
_colorRect.graphics.drawRect(x, y, width, height);
_colorRect.graphics.endFill();
}
Draws the colored rectangles and displays the textual color values based on mouse position.
private function displayColorData():void {
// only render if the mouse has been over an image
if (_colorPoint) {
_colorRect.graphics.clear();
// find color in first image based on mouse position
var color:uint = _bitmap0.bitmapData.getPixel(_colorPoint.x, _colorPoint.y);
_colorLabel0.text = formatColor(color);
drawColorRect(color, 0, 0, IMAGE_WIDTH, COLOR_RECT_HEIGHT);
// find color in second image based on mouse position
color = _bitmap1.bitmapData.getPixel(_colorPoint.x, _colorPoint.y);
_colorLabel1.text = formatColor(color);
drawColorRect(color, IMAGE_WIDTH, 0, IMAGE_WIDTH, COLOR_RECT_HEIGHT);
// draws all images, including the composite image, into a new BitmapData instance
// so that the pixel value from the composite image can be retrieved
var bitmapData:BitmapData = new BitmapData(IMAGE_WIDTH*3, IMAGE_HEIGHT);
bitmapData.draw(_bitmapHolder);
// find color in composite image based on mouse position
color = bitmapData.getPixel(_colorPoint.x + IMAGE_WIDTH*2, _colorPoint.y);
_colorLabel2.text = formatColor(color);
drawColorRect(color, IMAGE_WIDTH*2, 0, IMAGE_WIDTH, COLOR_RECT_HEIGHT);
bitmapData.dispose();
}
}
Stores the relative point of the mouse for each image so that hovering over
one of the three images will result in color data being retrieved from all three images.
private function setColorPoint(event:MouseEvent):void {
var x:Number = event.localX;
var y:Number = event.localY;
// use modulo to find an x position between 0 and IMAGE_WIDTH,
// no matter which image the mouse is over
x %= IMAGE_WIDTH;
_colorPoint = new Point(x, y);
}
Handler for when the shader completes loading.
parameter: event Event dispatched by URLLoader.
private function onShaderLoaded(event:Event):void {
var loader:URLLoader = event.target as URLLoader;
_shader = new Shader(loader.data as ByteArray);
}
Handler for when the blend mode label is clicked, applying new blend mode to composite image.
parameter: event Event dispatched by _blendModeLabel TextField.
private function onBlendModeClick(event:MouseEvent):void {
// find current blend mode index
var index:uint = _blendModes.indexOf(_blendModeLabel.text);
// make sure that index of blend mode in array is within range
if (++index >= _blendModes.length) {
index = 0;
}
var blendMode:String = _blendModes[index];
_blendModeLabel.text = blendMode;
// for the SHADER blend mode, apply shader to blendDhader property
if (blendMode == "SHADER") {
_blendedBitmap.blendShader = _shader;
}
_blendedBitmap.blendMode = BlendMode[blendMode];
// update color information
displayColorData();
}
Handler for when a new image file is selected to be loaded, initiating the load.
parameter: event Event dispatched by _file FileReference.
private function onFileSelect(event:Event):void {
_file.addEventListener(Event.COMPLETE, onImageLoadComplete);
_file.load();
}
Handler for when a new image file completes loading.
parameter: event Event dispatched by _file FileReference.
private function onImageLoadComplete(event:Event):void {
// have to use Loader to load bytes from the file data into a DisplayObject.
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLocalFileRead);
loader.loadBytes(_file.data);
}
Handler for when bytes from image file have been read into Loader instance.
parameter: event Event dispatched by Loader.
private function onLocalFileRead(event:Event):void {
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var bitmap:Bitmap = loaderInfo.content as Bitmap;
// places image data into appropriate Bitmap instance
_bitmapLoading.bitmapData = bitmap.bitmapData;
// replaces image in stacked composite as well
if (_bitmapLoading == _bitmap0) {
_bitmap2.bitmapData = bitmap.bitmapData;
} else {
_blendedBitmap.bitmapData = bitmap.bitmapData;
}
}
Handler for when bitmap holder is doublclicked, opening file browse dialog.
parameter: event Event dispatched by _bitmapHolder Sprite.
private function onHolderDoubleClick(event:MouseEvent):void {
var x:Number = stage.mouseX;
var y:Number = stage.mouseY;
_bitmapLoading = null;
// find which bitmap was clicked and store it
if (_bitmap0.hitTestPoint(x, y)) {
_bitmapLoading = _bitmap0;
} else if (_bitmap1.hitTestPoint(x, y)) {
_bitmapLoading = _bitmap1;
}
// only open browse if one of the two images was clicked (not the composite)
if (_bitmapLoading != null) {
_file = new FileReference();
_file.addEventListener(Event.SELECT, onFileSelect);
_file.browse([new FileFilter("Images", "*.jpg;*.gif;*.png")]);
}
}
Handler for when the mouse moves over the images. Updates the color data.
parameter: event Event dispatched by _bitmapHolder Sprite.
private function onHolderMouseMove(event:MouseEvent):void {
// update the color information if the mouse is not depressed
if (!_mouseDown) {
setColorPoint(event);
displayColorData();
event.updateAfterEvent();
}
}
Handler for when the mouse is pressed while over the images. Draws targets over each image.
parameter: event Event dispatched by _bitmapHolder Sprite.
private function onHolderMouseDown(event:MouseEvent):void {
// if the mouse was already down (and dragged off), update the color data now
if (_mouseDown) {
setColorPoint(event);
displayColorData();
}
_mouseDown = true;
var x:Number = _colorPoint.x;
var y:Number = _colorPoint.y;
// draws targets for the relative mouse position on all three images
var targetSize:uint = 5;
_targets.graphics.clear();
_targets.graphics.lineStyle(1, 0xFF0000);
for (var i:uint = 0; i < 3; i++) {
_targets.graphics.moveTo(x, y-targetSize);
_targets.graphics.lineTo(x, y+targetSize);
_targets.graphics.moveTo(x-targetSize, y);
_targets.graphics.lineTo(x+targetSize, y);
x +=IMAGE_WIDTH;
}
}
Handler for when the mouse is released while over the images. Removes the drawn targets.
parameter: event Event dispatched by _bitmapHolder Sprite.
private function onHolderMouseUp(event:MouseEvent):void {
_mouseDown = false;
_targets.graphics.clear();
}
Handler for when the initial images complete loading.
parameter: event Event dispatched by LoaderInfo.
private function onImageLoaded(event:Event):void {
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var bitmap:Bitmap = loaderInfo.content as Bitmap;
// scale the loaded images to match the size specified in the constants
var matrix:Matrix = new Matrix();
matrix.scale(IMAGE_WIDTH/bitmap.width, IMAGE_HEIGHT/bitmap.height);
var copiedImage:BitmapData = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT);
copiedImage.draw(bitmap.bitmapData, matrix);
bitmap = new Bitmap(copiedImage);
// blendedBitmap holds the composites on the right
var blendedBitmap:Bitmap = new Bitmap(copiedImage);
blendedBitmap.x = IMAGE_WIDTH*2;
// add loaded bitmap (now scaled) and the copy for the composite to the stage
_bitmapHolder.addChild(bitmap);
_bitmapHolder.addChild(blendedBitmap);
// if this is only first iamge, start loading of second
if (_bitmapHolder.numChildren == 2) {
_bitmap0 = bitmap;
_bitmap2 = blendedBitmap;
loadImage(IMAGE_1);
// otherwise add the UI and load the shader
} else {
_bitmap1 = bitmap;
_bitmap1.x = IMAGE_WIDTH;
_blendedBitmap = blendedBitmap;
addColorShapes();
addColorLabels();
addBlendModeLabel();
enableMouseEvents();
loadShader();
}
}
}
}
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.