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

Pixelate by BitmapData#fillRect

Get Adobe Flash player
by Aquioux 23 Jul 2012
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/wPwm
 */

package {
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Matrix;
    import flash.geom.Rectangle;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#FFFFFF")]
    /**
     * Pixelate by BitmapData#fillRect
     * Practice of strategy pattern
     * @author Aquioux(Yoshida, Akio)
     * @see http://aquioux.net/blog/?p=2617
     * Picture 「写真素材 足成」 http://www.ashinari.com/
     */
    public class Main extends Sprite {
        // Pixelation 共用パラメータ
        private const DEGREE:int = 5;        // 減色の段階数

        // Drawer 用パラメータ
        private const INTERVAL:int = 9;                // タイルの間隔
        private const COLOR_SHIFT:Number = 0.075;    // 表示色の変動値
        
        // ビューアを塗りつぶす色
        private const FILL_COLOR:uint = 0xFFFFFF;
        

        // カンバス
        private var canvas_:BitmapData;

        // 前回押したボタン
        private var prevButton_:PushButton;
        
        // 描画クラス
        private var drawer_:Drawer;
        // 描画ふるまいクラス格納配列
        private var behaviors_:Array;
        
        // 描画データ
        private var data_:Vector.<uint>;

        // BitmapData.rect
        private var sourceRect_:Rectangle;

        
        // コンストラクタ
        public function Main() {
            var url:String = "http://assets.wonderfl.net/images/related_images/9/9c/9c61/9c61a1edd9101f41eefb3a5a40a4e4601c27e16cm";
            var loader:Loader = new Loader();
            loader.load(new URLRequest(url), new LoaderContext(true));
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
        }
        private function completeHandler(event:Event):void {
            // ステージサイズ
            var sw:uint = stage.stageWidth;
            var sh:uint = stage.stageHeight;
            
            // ソースイメージ
            var loadedBmd:BitmapData = event.target.loader.content.bitmapData;
            var sourceBmd:BitmapData = new BitmapData(52, 52);
            sourceRect_ = sourceBmd.rect;
            sourceBmd.draw(loadedBmd, new Matrix(52/200, 0, 0, 52/200));
            
            // ビューアの生成
            canvas_ = new BitmapData(sw, sh, false, FILL_COLOR);
            addChild(new Bitmap(canvas_));
            // ソースイメージの表示
            addChild(new Bitmap(sourceBmd));
            
            // ピクセル化クラス
            Pixelation.degree = DEGREE;
            data_ = Pixelation.pixelate(sourceBmd.clone());
            
            // 描画クラス
            drawer_ = new Drawer();
            drawer_.interval   = INTERVAL;
            drawer_.colorShift = COLOR_SHIFT;
            // 描画ふるまいクラス
            behaviors_ = [
                "Horizontal", new BehaviorHorizontal(), 
                "Vertival",   new BehaviorVertical(),
                "Alter 1",    new BehaviorAlter1(),
                "Alter 2",    new BehaviorAlter2(),
                "Square",     new BehaviorSquare()
            ];

            // 描画切り替えボタン
            var buttonWidth:int  = 50;
            var buttonHeight:int = 20;
            var len:int = behaviors_.length / 2;
            for (var i:int = 0; i < len; i++) {
                var button:PushButton = new PushButton(this, sw - (buttonWidth * (len - i)), 0, behaviors_[i * 2], buttonHandler);
                button.width  = buttonWidth;
                button.height = buttonHeight;
                button.name   = String(i);
            }
        }
        
        // ボタンハンドラ
        private function buttonHandler(e:Event):void {
            if (prevButton_) prevButton_.enabled = true;
            var currentButton:PushButton = PushButton(e.target);
            currentButton.enabled = false;
            prevButton_ = currentButton;
            drawer_.behavior = behaviors_[int(currentButton.name) * 2 + 1];
            draw();
        }

        // 描画
        private function draw():void {
            // ビューアのクリア
            canvas_.fillRect(canvas_.rect, FILL_COLOR);
            // 描画実行
            drawer_.draw(data_, canvas_, sourceRect_);
        }
    }
}


//package {
    //import aquioux.display.bitmapDataEffector.GrayScale;
    //import aquioux.display.bitmapDataEffector.Posterize;
    //import aquioux.display.bitmapDataEffector.Smooth;
    import flash.display.BitmapData;
    import flash.filters.BitmapFilterQuality;
    /**
     * 入力された BitmapData のピクセル情報を取得して、青チャンネルから段階値を取得する
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Pixelation {
        /**
         * 減色の段階数
         */
        static public function set degree(value:int):void { _degree = value; }
        static private var _degree:int = 5;
        
        /**
         * ポジ・ネガ反転
         */
        static public function set invert(value:Boolean):void { _invert = value; }
        static private var _invert:Boolean = false;


        /**
         * 段階値取得
         * @param    bmd    入力 BitmapData
         * @return    イメージ全 pixel の段階値を格納した配列
         */
        static public function pixelate(bmd:BitmapData):Vector.<uint> {
            // イメージ操作フィルター生成と入力 BitmapData への適用
            // 平滑化
            var smooth:Smooth = new Smooth();
            smooth.strength = 1;
            smooth.quality = BitmapFilterQuality.HIGH;
            smooth.applyEffect(bmd);
            // グレイスケール
            new GrayScale().applyEffect(bmd);
            // 減色
            var posterize:Posterize = new Posterize();
            posterize.degree = _degree + 1;
            posterize.applyEffect(bmd);
            
            // bmd.getVector
            var pixelVector:Vector.<uint> = bmd.getVector(bmd.rect);
            pixelVector.fixed = true;

            // 減色の段階値を計算
            var val:Number = 0xFF / _degree;
            var len:uint   = pixelVector.length;
            var data:Vector.<uint>  = new Vector.<uint>();
            for (var i:int = 0; i < len; i++) {
                var result:Number = (pixelVector[i] & 0xFF) / val;
                if (result != int(result)) int(result + 1);
                data.push(_invert ? result : _degree - result);
            }
            data.fixed = true;
            return data;
        }
    }
//}


//package  {
    //import aquioux.display.colorUtil.CycleRGB;
    import flash.display.BitmapData;
    import flash.geom.Rectangle;
    /**
     * Pixelation で得られたデータを描画
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Drawer {
        /**
         * タイルの間隔
         */
        public function set interval(value:uint):void { _interval = value; }
        private var _interval:int = 5;
        
        /**
         * 表示色の変動値
         */
        public function set colorShift(value:Number):void { _colorShift = value; }
        private var _colorShift:Number = 0.1;
        
        /**
         * 描画ふるまいクラス
         */
        public function set behavior(value:IBehavior):void { _behavior = value; }
        private var _behavior:IBehavior;
        

        /**
         * コンストラクタ
         */
        public function Drawer() {
            _behavior = new BehaviorSquare();
        }

        /**
         * 描画
         * @param    data    表示のためのデータ
         * @param    canvas    表示カンバス
         * @param    rect    表示領域
         */
        public function draw(data:Vector.<uint>, canvas:BitmapData, rect:Rectangle):void {
            var width:uint   = rect.width;
            var angle:Number = Math.random() * 360;
            var len:uint     = data.length;
            canvas.lock();
            for (var i:int = 0; i < len; i++) {
                var currentDegree:int = data[i] * 2 - 1;
                if (currentDegree < 0) currentDegree = 0;
                var posX:int =  i % width;
                var posY:int = (i / width) >> 0;
                if (currentDegree > 0) canvas.fillRect(_behavior.execute(currentDegree, posX, posY, _interval), CycleRGB.getColor(angle));
                angle += _colorShift;
            }
            canvas.unlock();
        }
    }
//}


//package {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラスの interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        function execute(degree:int, posX:int, posY:int, interval:int):Rectangle;
    }
//}
//package  {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラス(水平)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class BehaviorHorizontal implements IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        public function execute(degree:int, posX:int, posY:int, interval:int):Rectangle {
            var shiftX:int = 0;
            var shiftY:int = (interval - degree) / 2;
            var rect:Rectangle = new Rectangle();
            rect.x = posX * interval + shiftX;
            rect.y = posY * interval + shiftY;
            rect.width  = interval;
            rect.height = degree;
            return rect;
        }
    }
//}
//package  {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラス(垂直)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class BehaviorVertical implements IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        public function execute(degree:int, posX:int, posY:int, interval:int):Rectangle {
            var shiftX:int = (interval - degree) / 2;
            var shiftY:int = 0;
            var rect:Rectangle = new Rectangle();
            rect.x = posX * interval + shiftX;
            rect.y = posY * interval + shiftY;
            rect.width  = degree;
            rect.height = interval;
            return rect;
        }
    }
//}
//package  {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラス(水平と垂直の交互)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class BehaviorAlter1 implements IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        public function execute(degree:int, posX:int, posY:int, interval:int):Rectangle {
            var shiftX:int;
            var shiftY:int;
            var rect:Rectangle = new Rectangle();
            if (posX % 2) {
                // same BehaviorHorizontal
                shiftX = 0;
                shiftY = (interval - degree) / 2;
                rect.width  = interval;
                rect.height = degree;
            } else {
                // same BehaviorVertical
                shiftX = (interval - degree) / 2;
                shiftY = 0;
                rect.width  = degree;
                rect.height = interval;
            }
            rect.x = posX * interval + shiftX;
            rect.y = posY * interval + shiftY;
            return rect;
        }
    }
//}
//package  {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラス(水平と垂直の交互)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class BehaviorAlter2 implements IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        public function execute(degree:int, posX:int, posY:int, interval:int):Rectangle {
            var shiftX:int;
            var shiftY:int;
            var rect:Rectangle = new Rectangle();
            if ((posX % 2 + posY % 2) % 2) {
                // same BehaviorHorizontal
                shiftX = 0;
                shiftY = (interval - degree) / 2;
                rect.width  = interval;
                rect.height = degree;
            } else {
                // same BehaviorVertical
                shiftX = (interval - degree) / 2;
                shiftY = 0;
                rect.width  = degree;
                rect.height = interval;
            }
            rect.x = posX * interval + shiftX;
            rect.y = posY * interval + shiftY;
            return rect;
        }
    }
//}
//package  {
    import flash.geom.Rectangle;
    /**
     * 描画ふるまいクラス(正方形)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class BehaviorSquare implements IBehavior {
        /**
         * ふるまいの実行(描画のための Rectangle を生成)
         * @param    degree    段階
         * @param    posX    開始X座標
         * @param    posY    開始Y座標
         * @param    interval    タイルの間隔
         * @return    描画のための Rectangle
         */
        public function execute(degree:int, posX:int, posY:int, interval:int):Rectangle {
            var shiftX:int = (interval - degree) / 2;
            var shiftY:int = (interval - degree) / 2;
            var rect:Rectangle = new Rectangle();
            rect.x = posX * interval + shiftX;
            rect.y = posY * interval + shiftY;
            rect.width  = degree;
            rect.height = degree;
            return rect;
        }
    }
//}


//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.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;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    /**
     * BlurFilter による平滑化
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Smooth implements IEffector {
        /*
         * ぼかしの強さ
         * @param    value    数値
         */
        public function set strength(value:Number):void {
            filter_.blurX = filter_.blurY = value;
        }
        /*
         * ぼかしの質
         * @param    value    数値
         */
        public function set quality(value:int):void {
            filter_.quality = value;
        }

        // ブラーフィルタ
        private var filter_:BlurFilter;

        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;


        /*
         * コンストラクタ
         */
        public function Smooth() {
            filter_ = new BlurFilter(2, 2, BitmapFilterQuality.MEDIUM);
        }
        
        /*
         * 効果適用
         * @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.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;
    }
//}


//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);
        }
    }
//}