In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

02-01_BlendModes

Get Adobe Flash player
by amashio 06 Aug 2010
    Embed
/**
 * 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();
            }
        }

    }

}