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

fadeout photochromic のパチモン

カメラの前でポーズをキメたら、"SHOOT" ボタンを押してください。
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jztz
 */

package {
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import net.hires.debug.Stats;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
    
    /**
     * fadeout photochromic のパチモン
     * @see http://www.youtube.com/watch?v=Mt_4mfuwTAU
     * @see http://www.youtube.com/watch?v=5hmmjEhqS8k
     * @see http://aquioux.net/blog/?p=2398
     * @author Aquioux(Yoshida, Akio)
     */
    public class Main extends Sprite {
        
        private var webCam_:WebCamera;            // ウェブカメラ
        private var viewer_:Bitmap;                // ウェブカメラビューア
        private var shootButton_:PushButton;    // 撮影ボタン
        private var fadeoutImage_:FadeoutImage;    // フェードアウトイメージ
        
        private const CAMERA_WIDTH:int  = 156;
        private const CAMERA_HEIGHT:int = 156;
        private const DISPLAY_SCALE:int = 3;

        public function Main() {
            // ウェブカメラ
            try {
                webCam_ = new WebCamera(CAMERA_WIDTH, CAMERA_HEIGHT, this.stage.frameRate);
            } catch (err:Error) {
                throw new Error(err.message);
                return;
            }
            // ウェブカメラのイベント登録
            webCam_.addEventListener(Event.COMPLETE, completeWebCameraHandler);
            
            // ビューア
            viewer_ = new Bitmap(new BitmapData(CAMERA_WIDTH, CAMERA_HEIGHT, true, 0x0));
            viewer_.scaleX =  DISPLAY_SCALE;
            viewer_.scaleY =  DISPLAY_SCALE;
            viewer_.x      = -DISPLAY_SCALE;
            viewer_.y      = -DISPLAY_SCALE;
            addChild(viewer_);
        }
        
        // WebCamera の Event.COMPLETE を受け取る
        private function completeWebCameraHandler(e:Event):void {
            e.target.removeEventListener(Event.ACTIVATE, arguments.callee);

            // 撮影ボタン
            shootButton_ = new PushButton(this, 0, 0, "SHOOT", buttonHandler);
            shootButton_.width = 50;
            shootButton_.x = stage.stageWidth - shootButton_.width;
            shootButton_.y = stage.stageHeight - shootButton_.height;

            addEventListener(Event.ENTER_FRAME, update);
        }
        // 更新
        private function update(e:Event):void {
            viewer_.bitmapData = webCam_.update();
        }

        // ボタンハンドラ
        private function buttonHandler(e:MouseEvent):void {
            // 諸々後始末
            removeChild(shootButton_);
            removeEventListener(Event.ENTER_FRAME, update);
            removeChild(viewer_);

            // フェードアウトイメージ
            fadeoutImage_ = new FadeoutImage(viewer_.bitmapData);
            fadeoutImage_.addEventListener(Event.COMPLETE, completeFadeoutImageHandler);
            fadeoutImage_.displayScale = DISPLAY_SCALE;
            fadeoutImage_.x = -Math.ceil(DISPLAY_SCALE / 2);
            fadeoutImage_.y = -Math.ceil(DISPLAY_SCALE / 2);
            addChild(fadeoutImage_);
        }
        // 描画
        private function completeFadeoutImageHandler(e:Event):void {
            fadeoutImage_.draw();
        }
    }
}

//package {
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.IBitmapDrawable;
    import flash.events.ActivityEvent;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.Camera;
    import flash.media.Video;

    /**
     * WebCamera
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class WebCamera extends EventDispatcher {
        private var bitmapData_:BitmapData;    // ウェブカメラの画像
        private var video_:Video;            // ビデオ
        private var matrix_:Matrix;            // 鏡像になるようにさせるための Matrix
        
        /**
         * コンストラクタ
         * @param    cameraWidth        カメラ幅
         * @param    cameraHeight    カメラ高
         */
        public function WebCamera(cameraWidth:int, cameraHeight:int, frameRate:int = 30) {
            // 外部へ渡す BitmapData の生成
            bitmapData_ = new BitmapData(cameraWidth, cameraHeight, true, 0x0);
            
            // 鏡像になるよう、Matrix で左右反転
            matrix_ = new Matrix(-1, 0, 0, 1, cameraWidth, 0);
            
            // カメラ準備
            var camera:Camera = Camera.getCamera();
            if (camera) {
                // camera のセットアップ
                camera.setMode(cameraWidth, cameraHeight, frameRate);
                // video のセットアップ
                video_ = new Video(cameraWidth, cameraHeight);
                video_.attachCamera(camera);
                // カメラがアクティブになるまで待つ
                camera.addEventListener(ActivityEvent.ACTIVITY, activeHandler);
            } else {
                throw new Error("カメラがありません。");
            }
        }
        // ACTIVITY イベントハンドラ
        private function activeHandler(e:ActivityEvent):void {
            e.target.removeEventListener(ActivityEvent.ACTIVITY, arguments.callee);
            dispatchEvent(new Event(Event.COMPLETE));
        }
        
        /**
         * 更新
         */
        public function update():BitmapData {
            bitmapData_.draw(video_, matrix_);
            return bitmapData_;
        }
    }
//}

//package {
    //import aquioux.display.colorUtil.CycleRGB;
    //import aquioux.display.bitmapDataEffector.GrayScale;
    //import aquioux.display.bitmapDataEffector.Posterize;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.ColorTransform;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    /**
     * FadeoutImage
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class FadeoutImage extends Sprite {
        /**
         * ビューア
         */
        public function set viewer(value:Bitmap):void { _viewer = value; }
        private var _viewer:Bitmap;
        
        /**
         * 表示スケール
         */
        public function set displayScale(value:int):void { _displayScale = value; }
        private var _displayScale:int = 3;
        
        private const DEGREE:int = 24;
        
        private const FADE_VALUE:Number = 0.97;
        private const FADE:ColorTransform = new ColorTransform(FADE_VALUE, FADE_VALUE, FADE_VALUE);
        
        private const ZERO_POINT:Point = new Point();
        
        private var inputBmd_:BitmapData;
        private var bufferBmd_:BitmapData;
        private var viewBmd_:BitmapData;

        private var rect_:Rectangle;
        
        private var color_:uint;
        
        private var vectors_:Vector.<Vector.<uint>>;
        private var vector_:Vector.<uint>
        
        private var cntDraw_:int = 0;
        
        private var cntUpdate_:int;
        private var cntLen_:int;
        
        public function FadeoutImage(bmd:BitmapData) {
            inputBmd_ = bmd;
            addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        }
        private function addedToStageHandler(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, arguments.callee);

            // サイズの取得
            var imageWidth:int  = inputBmd_.width;
            var imageHeight:int = inputBmd_.height;

            // 各 BitmapData 生成
            bufferBmd_ = new BitmapData(imageWidth, imageHeight, true, 0x00000000);
            viewBmd_   = new BitmapData(imageWidth, imageHeight, true, 0xFF000000);
            rect_  = inputBmd_.rect;
            
            // ビューア
            var viewer:Bitmap = new Bitmap(viewBmd_);
            viewer.scaleX = _displayScale;
            viewer.scaleY = _displayScale;
            viewer.x = (stage.stageWidth  - viewer.width)  / 2;
            viewer.y = (stage.stageHeight - viewer.height) / 2;
            addChild(viewer);
            
            // イメージデータフィルター生成と適用
            new GrayScale().applyEffect(inputBmd_);
            var posterize:Posterize = new Posterize();
            posterize.degree = DEGREE;
            posterize.applyEffect(inputBmd_);
            var gradation:Vector.<uint> = posterize.gradation;
            
            // 表示のためのデータ生成
            vectors_ = new Vector.<Vector.<uint>>();
            var bmdVector:Vector.<uint> = new Vector.<uint>();
            var positionVector:Vector.<uint> = new Vector.<uint>();
            for (var i:int = 0; i < DEGREE; i++) {
                var threshold:uint = gradation[i];
                bufferBmd_.fillRect(bufferBmd_.rect, 0x00000000);
                bufferBmd_.threshold(inputBmd_, rect_, ZERO_POINT, "==", threshold, 0xFFFFFFFF, 0xFF, false);
                bmdVector = bufferBmd_.getVector(rect_);
                var c:int = 0;
                for (var y:int = 0; y < imageHeight; y++) {
                    for (var x:int = 0; x < imageWidth; x++) {
                        if (bmdVector[c++] == 0xFFFFFFFF) positionVector.push(x, y);
                    }
                }
                var vector:Vector.<uint> = new Vector.<uint>();
                vector = positionVector.concat();
                vector.fixed = true;
                vectors_[i] = vector;
                positionVector.length = 0;
            }
            
            // 入力イメージデータ削除
            inputBmd_.dispose();

            // 表示色決定
            color_ = CycleRGB.getColor32(Math.random() * 360 >> 0);
            
            // COMPLETE イベント発効
            dispatchEvent(new Event(Event.COMPLETE));
        }
        
        public function draw():void {
            bufferBmd_.lock();
            viewBmd_.lock();

            bufferBmd_.fillRect(rect_, 0x00000000);
            viewBmd_.colorTransform(rect_, FADE);

            vector_ = vectors_[cntDraw_++];
            cntLen_ = vector_.length;
            cntUpdate_ = 0;
            addEventListener(Event.ENTER_FRAME, update);

            bufferBmd_.unlock();
            viewBmd_.unlock();
        }
        private function update(e:Event):void {
            var posX:uint;
            var posY:uint;
            var c:int = 5;    // 一度の update で表示する pixel 数
            while (--c) {
                if (cntUpdate_ < cntLen_) {
                    posX = vector_[cntUpdate_];
                    posY = vector_[++cntUpdate_];
                    bufferBmd_.setPixel32(posX, posY, color_);
                    ++cntUpdate_;
                } else {
                    break;
                }
            }
            viewBmd_.draw(bufferBmd_, null, null, BlendMode.LIGHTEN);
            if (cntUpdate_ >= cntLen_) {
                removeEventListener(Event.ENTER_FRAME, arguments.callee);
                if (cntDraw_ < DEGREE) draw();
            }
        }
    }
//}

//package aquioux.display.colorUtil {
    /**
     * コサインカーブで色相環的な RGB を計算
     * @author Aquioux(YOSHIDA, Akio)
     */
    /*public*/ class CycleRGB {
        /**
         * 32bit カラーのためのアルファ値(0~255)
         */
        static public function get alpha():uint { return _alpha; }
        static public function set alpha(value:uint):void {
            _alpha = (value > 0xFF) ? 0xFF : value;
        }
        private static var _alpha:uint = 0xFF;
    
        private static const PI:Number = Math.PI;        // 円周率
        private static const DEGREE120:Number  = PI * 2 / 3;    // 120度(弧度法形式)
        
        /**
         * 角度に応じた RGB を得る
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNN)
         */
        public static function getColor(angle:Number):uint {
            var radian:Number = angle * PI / 180;
            var r:uint = (Math.cos(radian)             + 1) * 0xFF >> 1;
            var g:uint = (Math.cos(radian + DEGREE120) + 1) * 0xFF >> 1;
            var b:uint = (Math.cos(radian - DEGREE120) + 1) * 0xFF >> 1;
            return r << 16 | g << 8 | b;
        }
        
        /**
         * 角度に応じた RGB を得る(32bit カラー)
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNNNN)
         */
        public static function getColor32(angle:Number):uint {
            return _alpha << 24 | getColor(angle);
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.filters.ColorMatrixFilter;
    import flash.geom.Point;
    /**
     * ColorMatrixFilter による BitmapData のグレイスケール化(NTSC 系加重平均による)
     * 参考:Foundation ActionScript 3.0 Image Effects(P106)
     *         http://www.amazon.co.jp/gp/product/1430218711?ie=UTF8&tag=laxcomplex-22
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class GrayScale implements IEffector {
        private const R:Number = EffectorUtils.LUM_R;
        private const G:Number = EffectorUtils.LUM_G;
        private const B:Number = EffectorUtils.LUM_B;

        private const MATRIX:Array = [
            R, G, B, 0, 0,
            R, G, B, 0, 0,
            R, G, B, 0, 0,
            0, 0, 0, 1, 0
        ];
        private const FILTER:ColorMatrixFilter = new ColorMatrixFilter(MATRIX);
        
        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
        
        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, FILTER);
            return value;
        }
    }
//}


//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.geom.Point;
    /**
     * paletteMap による BitmapData の減色
     * 「実践画像処理入門」 培風館 内村圭一・上瀧剛 P16 「2.5 濃度値の量子化による減色処理」
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Posterize implements IEffector {
        /*
         * 減色の段階
         * @param    value    段階
         */
        public function set degree(value:uint):void {
            // value の有効範囲は 2 ~ 256
            if (value <   2) value =   2;
            if (value > 256) value = 256;
            
            if (_gradation) {
                _gradation.fixed = false;
                _gradation.length = 0;
            } else {
                _gradation = new Vector.<uint>();
            }

            var prevVal:uint = 0xFF;
            for (var i:int = 0; i < 256; i++) {
                var val:uint = uint(i / (256 / value)) * 255 / (value - 1);
                rArray_[i] = val << 16;
                gArray_[i] = val <<  8;
                bArray_[i] = val;
                
                if (prevVal != val) {
                    _gradation.push(val);
                    prevVal = val;
                }
            }
            _gradation.fixed = true;
        }
        
        // 減色化によって計算された gradation の値を格納する Vector
        // degree を set することではじめて有効になる
        // length は degree になる
        public function get gradation():Vector.<uint> { return _gradation; }
        private var _gradation:Vector.<uint>;

        //
        // paletteMap の引数となる各 Channel 用の Array
        private var rArray_:Array = [];
        private var gArray_:Array = [];
        private var bArray_:Array = [];
        
        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
        
        /*
         * コンストラクタ
         */
        public function Posterize() {
            degree = 8;    // degree のデフォルト
        }
        
        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.paletteMap(value, value.rect, ZERO_POINT, rArray_, gArray_, bArray_);
            return value;
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    /**
     * BitmapDataEffector 用 interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface IEffector {
        function applyEffect(value:BitmapData):BitmapData;
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.geom.Point;
    /**
     * bitmapDataEffector パッケージ内のクラスで共通に使う定数など
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class EffectorUtils {
        // BitmapData が備える各種メソッドの destPoint 用
        static public const ZERO_POINT:Point = new Point(0, 0);
        
        // グレイスケール用の各チャンネルの重みづけ
        // NTSC系加重平均法(YIQ,YCbCr も同じ)
        static public const LUM_R:Number = 0.298912;
        static public const LUM_G:Number = 0.586611;
        static public const LUM_B:Number = 0.114478;
    }
//}