02-01_BlendModes
/**
* Copyright amashio ( http://wonderfl.net/user/amashio )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/4As1
*/
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)]
/**
* ブレンドモードの適用をテストするクラス。2つのイメージをロード、表示し、それらをコピーしてステージの右に重ねる。
* ブレンドモードの名前を表示するテキストフィールドをクリックすると、ブレンドモードが変更される。3つのイメージ
* (2つはロードしたもので、1つは重ねて合成したもの)すべてで、 マウスの下にあるカラーがイメージの下部に表示される。
* イメージをダブルクリックすると、アプリケーションに新しいイメージをロードできる
*/
public class BlendModes extends Sprite {
// アセット用の相対ディレクトリを変えるときにはこれを更新
private static const ASSETS_DIRECTORY:String = "http://assets.wonderfl.net/images/related_images/";
// 別のシェーダファイルを使用するときにはこれを更新
private static const SHADER:String = ASSETS_DIRECTORY + "luminosity.pbj";
// デフォルトのイメージを変える場合にはこれらを更新
private static const IMAGE_0:String = ASSETS_DIRECTORY + "d/d3/d319/d319d4212f3df40b118069a6bd32f16aed317608";
private static const IMAGE_1:String = ASSETS_DIRECTORY + "a/aa/aab5/aab5f166fa98ad41beb3b40f9e9973d654f75eb5";
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;
/**
* コンストラクタ。init()を呼び出すだけ
*/
public function BlendModes() {
init();
}
/**
* ビットマップを保持するビットマップホルダーを初期化し、ブレンドモードの配列を作成して、1つめのイメージのロードを開始I
*/
private function init():void {
_bitmapHolder = new Sprite();
_bitmapHolder.doubleClickEnabled = true;
addChild(_bitmapHolder);
_blendModes = [];
// describeType()とE4Xを使って、BlednModeに関してdescribeType()が返すXMLからconstantノードを取得し
// このスプライトで使用できる全ブレンドモードを求める
var blendModes:XMLList = describeType(BlendMode).constant;
// 返されたブレンドモードの名前である定数の情報を走査
for each (var blendMode:XML in blendModes) {
// 定数名を配列に追加
_blendModes.push(blendMode.@name.toString());
}
loadImage(IMAGE_0);
}
/**
* イメージのロード
*
* @param imagePath ロードするイメージへのパス
*/
private function loadImage(imagePath:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.load(new URLRequest(imagePath));
}
/**
* クラスの定数で指定されたシェーダをロード
*/
private function loadShader():void {
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onShaderLoaded);
loader.load(new URLRequest(SHADER));
}
/**
* 指定された座標で空のシェイプを表示リストに追加
*
* @param x シェイプのx位置
* @param y シェイプのy位置
*
* @return 作成したシェイプ
*/
private function addShape(x:Number, y:Number):Shape {
var shape:Shape = new Shape();
shape.x = x;
shape.y = y;
addChild(shape);
return shape;
}
/**
* マウスでなぞったピクセルのカラーを描画するシェイプを矩形として追加し、
* また各イメージ内でマウス位置を示すターゲットを描画するシェイプも追加
*/
private function addColorShapes():void {
_colorRect = addShape(0, IMAGE_HEIGHT);
_targets = addShape(0, 0);
}
/**
* 指定されたxとy位置に指定されたTextFormatでテキストフィールドを追加
*
* @param format テキストフィールドに適用するTextFormat
* @param x テキストフィールドのx位置
* @param y テキストフィールドのy位置
*
* @return 作成したテキストフィールド
*/
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;
}
/**
* マウス位置にもとづき各イメージからの16進数カラー値を表示する3つのテキストフィールドを追加
*/
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);
// カラー値がうまく揃うようにフォーマットにタブストップを使用
format.tabStops = [50];
// 各イメージごとに1つ、計3つのテキストフィールドを配置
_colorLabel0 = addTextField(format, x, y);
x += width;
_colorLabel1 = addTextField(format, x, y);
x += width;
_colorLabel2 = addTextField(format, x, y);
}
/**
* 現在のブレンドモードを表示する、クリック可能なテキストフィールドを追加
*/
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;
// 最初のブレンドモードの表示
_blendModeLabel.text = _blendModes[0];
// ブレンドモードを変更できるように、ラベルでクリックを可能にする
_blendModeLabel.addEventListener(MouseEvent.CLICK, onBlendModeClick);
addChild(_blendModeLabel);
}
/**
* ビットマップホルダーでのマウスイベントを有効にする
*/
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);
}
/**
* 16進数値と10進数のrgbの個々の値を持つストリングを返す
*
* @param color カラーを表す10進数値
*
* @return 16進数値とrgb値を含むストリング
*/
private function formatColor(color:uint):String {
var colorString:String = color.toString(16).toUpperCase();
// 左に0を加えて、確実に6桁にする
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;
}
/**
* 指定された位置に着色した矩形を描画する
*
* @param color 塗りに使用するカラー
* @param x 矩形のx位置
* @param y 矩形のy位置
* @param width 矩形の幅
* @param height 矩形の高さ
*/
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();
}
/**
* マウス位置にもとづき着色した矩形を描画し、テキストのカラー値を表示する
*/
private function displayColorData():void {
// マウスがイメージ上にあるときのみレンダリング
if (_colorPoint) {
_colorRect.graphics.clear();
// マウス位置にもとづきまず1つめのイメージのカラーを求める
var color:uint = _bitmap0.bitmapData.getPixel(_colorPoint.x, _colorPoint.y);
_colorLabel0.text = formatColor(color);
drawColorRect(color, 0, 0, IMAGE_WIDTH, COLOR_RECT_HEIGHT);
// マウス位置にもとづき2つめのイメージのカラーを求める
color = _bitmap1.bitmapData.getPixel(_colorPoint.x, _colorPoint.y);
_colorLabel1.text = formatColor(color);
drawColorRect(color, IMAGE_WIDTH, 0, IMAGE_WIDTH, COLOR_RECT_HEIGHT);
// 合成画像のピクセル値が取得できるように、合成画像を含むすべてのイメージを新しいBitmapDataインスタンスに描画
var bitmapData:BitmapData = new BitmapData(IMAGE_WIDTH*3, IMAGE_HEIGHT);
bitmapData.draw(_bitmapHolder);
// マウス位置にもとづき合成画像のカラーを求める
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();
}
}
/**
* 各イメージに関してマウスの相対位置を保持する。これにより、
* 3つのイメージのどれかをなぞると、3つすべてのイメージからのカラーデータが得られるようになる
*/
private function setColorPoint(event:MouseEvent):void {
var x:Number = event.localX;
var y:Number = event.localY;
// どのイメージをマウスオーバーしている場合でも、
// 剰余演算を使って0とIMAGE_WIDTH間のx位置を求める
x %= IMAGE_WIDTH;
_colorPoint = new Point(x, y);
}
/**
* シェーダのロードが完了したときのハンドラ
*
* @param event URLLoaderが送出するイベント
*/
private function onShaderLoaded(event:Event):void {
var loader:URLLoader = event.target as URLLoader;
_shader = new Shader(loader.data as ByteArray);
}
/**
* ブレンドモードのラベルがクリックされたときのハンドラ。合成画像に新しいブレンドモードを適用
*
* @param event TextFieldの_blendModeLabel が送出するイベント
*/
private function onBlendModeClick(event:MouseEvent):void {
// 現在のブレンドモードのインデックスを求める
var index:uint = _blendModes.indexOf(_blendModeLabel.text);
// indexが確実にその配列の範囲内におさまるようにする
if (++index >= _blendModes.length) {
index = 0;
}
var blendMode:String = _blendModes[index];
_blendModeLabel.text = blendMode;
// SHADERブレンドモードでは、シェーダをblendShaderプロパティに適用
if (blendMode == "SHADER") {
_blendedBitmap.blendShader = _shader;
}
_blendedBitmap.blendMode = BlendMode[blendMode];
// カラー情報の更新
displayColorData();
}
/**
* ロードする新しいイメージファイルが選択されたときのハンドラ。ロードを初期化
*
* @param event FileReferenceの_fileが送出するイベント
*/
private function onFileSelect(event:Event):void {
_file.addEventListener(Event.COMPLETE, onImageLoadComplete);
_file.load();
}
/**
* 新しいイメージファイルのロードが完了したときのハンドラ
*
* @param event FileReferenceの_fileが送出するイベント
*/
private function onImageLoadComplete(event:Event):void {
// Loaderを使って、ファイルデータのバイトをDisplayObjectにロードする必要がある
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLocalFileRead);
loader.loadBytes(_file.data);
}
/**
* イメージファイルのバイトがLoaderインスタンスに読み込まれたときのハンドラ
*
* @param event Loaderが送出するイベント
*/
private function onLocalFileRead(event:Event):void {
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var bitmap:Bitmap = loaderInfo.content as Bitmap;
// イメージデータを適切なBitmapインスタンスに配置
_bitmapLoading.bitmapData = bitmap.bitmapData;
// replaces image in stacked composite as well
if (_bitmapLoading == _bitmap0) {
_bitmap2.bitmapData = bitmap.bitmapData;
} else {
_blendedBitmap.bitmapData = bitmap.bitmapData;
}
}
/**
* ビットマップホルダーがダブルクリックされたときのハンドラ。ファイル参照ダイアログボックスを開く
*
* @param event Spriteの_bitmapHolderが送出するイベント
*/
private function onHolderDoubleClick(event:MouseEvent):void {
var x:Number = stage.mouseX;
var y:Number = stage.mouseY;
_bitmapLoading = null;
// どのビットマップがクリックされたかを調べ、それを保持
if (_bitmap0.hitTestPoint(x, y)) {
_bitmapLoading = _bitmap0;
} else if (_bitmap1.hitTestPoint(x, y)) {
_bitmapLoading = _bitmap1;
}
// 開くのは2つのイメージのどちらかがクリックされたときのみ(合成画像では開かない)
if (_bitmapLoading != null) {
_file = new FileReference();
_file.addEventListener(Event.SELECT, onFileSelect);
_file.browse([new FileFilter("Images", "*.jpg;*.gif;*.png")]);
}
}
/**
* マウスがイメージ上を移動したときのハンドラ。カラーデータを更新
*
* @param event Spriteの_bitmapHolderが送出するイベント
*/
private function onHolderMouseMove(event:MouseEvent):void {
// マウスが押下げられていない場合にカラー情報を更新
if (!_mouseDown) {
setColorPoint(event);
displayColorData();
event.updateAfterEvent();
}
}
/**
* マウスがイメージ上にあるとき押下げられたときのハンドラ。各イメージ上にターゲットを描画
*
* @param event Spriteの_bitmapHolderが送出するイベント
*/
private function onHolderMouseDown(event:MouseEvent):void {
// マウスがすでに押下げられている(かつ領域外までドラッグしていない)場合にはカラーデータをすぐに更新
if (_mouseDown) {
setColorPoint(event);
displayColorData();
}
_mouseDown = true;
var x:Number = _colorPoint.x;
var y:Number = _colorPoint.y;
// 3つのすべてのイメージ上に相対的にマウス位置を表すターゲットを描画
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;
}
}
/**
* マウスがイメージ上にある間にマウスがリリースされたときのハンドラ。描画したターゲットを削除
*
* @param event Spriteの_bitmapHolderが送出するイベント
*/
private function onHolderMouseUp(event:MouseEvent):void {
_mouseDown = false;
_targets.graphics.clear();
}
/**
* 最初のイメージのロードが完了したときのハンドラ
*
* @param event LoaderInfoが送出するイベント
*/
private function onImageLoaded(event:Event):void {
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var bitmap:Bitmap = loaderInfo.content as Bitmap;
// 定数で指定されたサイズに一致するように、ロードしたイメージを伸縮
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は右の合成画像を保持
var blendedBitmap:Bitmap = new Bitmap(copiedImage);
blendedBitmap.x = IMAGE_WIDTH*2;
// ロードした(伸縮済みの)ビットマップを追加し、合成画像のコピーを追加
_bitmapHolder.addChild(bitmap);
_bitmapHolder.addChild(blendedBitmap);
// これが最初のイメージである場合のみ、2つめのイメージのロードを開始
if (_bitmapHolder.numChildren == 2) {
_bitmap0 = bitmap;
_bitmap2 = blendedBitmap;
loadImage(IMAGE_1);
// そうでない場合には、UIを追加しシェーダをロード
} else {
_bitmap1 = bitmap;
_bitmap1.x = IMAGE_WIDTH;
_blendedBitmap = blendedBitmap;
addColorShapes();
addColorLabels();
addBlendModeLabel();
enableMouseEvents();
loadShader();
}
}
}
}