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

ウェブカム・エフェクト forked from: nengafl

other license, write as code comments
求む!ウェブカム作品
新しいエフェクタを作って、クールなウェブカム作品で萌えませんか?
書き換える場所はふたつです
1. ドキュメントクラス(Nengafl)のコンストラクタの最初の部分
chain.addEffector() メソッドで新しいエフェクタ・インスタンスを登録する
2. 独自 EffectorFormat の生成
新しい Effector クラスは EffectorFormat を書き換える
注意:EffectorFormat は AbstractEffector の後に記述してあります。
Nengafl の直後じゃなくてスイマセン(そうしないと参照問題でエラーになるので)。
http://aquioux.blog48.fc2.com/blog-entry-670.html に若干の説明を書きましたので、よろしければご参照ください。
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/tSFn
 */

// forked from nengafl's nengafl
// other license, write as code comments
// 求む!ウェブカム作品
// 新しいエフェクタを作って、クールなウェブカム作品で萌えませんか?
//
// 書き換える場所はふたつです
// 1. ドキュメントクラス(Nengafl)のコンストラクタの最初の部分
//    chain.addEffector() メソッドで新しいエフェクタ・インスタンスを登録する
// 2. 独自 EffectorFormat の生成
//    新しい Effector クラスは EffectorFormat を書き換える
//
// 注意:EffectorFormat は AbstractEffector の後に記述してあります。
//     Nengafl の直後じゃなくてスイマセン(そうしないと参照問題でエラーになるので)。
//
// http://aquioux.blog48.fc2.com/blog-entry-670.html に若干の説明を書きましたので、よろしければご参照ください。
//
package {
	import flash.display.Sprite;
	import flash.events.Event;
	/**
	 * 各種フィルタを使ったウェブカム画像の加工
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
	
	public class Nengafl extends Sprite {
		
		public function Nengafl() {
			// エフェクタ連鎖
			var chain:ChainEffectors = new ChainEffectors();
			// ===== 書き換える場所 ここから =====
			// ここで各エフェクタ・インスタンスを生成
			var smooth:EffectorSmoothing = new EffectorSmoothing();
			smooth.strength = 16;
			var posterize:EffectorPosterization = new EffectorPosterization();
			posterize.degree = 8;
			var sharp:EffectorSharp = new EffectorSharp();
			sharp.strength = 5;
			//var mosaic:EffectorPixelization = new EffectorPixelization(465, 465);
			//mosaic.size = 31;

			// addEffector で登録した順番で映像は加工される
			// 同じフィルタを使っても、順番を入れ替えるだけでずいぶん変わる
			chain.addEffector(smooth);
			chain.addEffector(posterize);
			//chain.addEffector(mosaic);
			chain.addEffector(sharp);
			//chain.addEffector(new EffectorNegative());
			//chain.addEffector(new EffectorGrayScale());
			// ===== 書き換える場所 ここまで =====
		
			// Model を生成
			try {
				var model:Model = new Model(stage);
			} catch (err:Error) {
				trace(err.message);
				return;
			}
			// View を生成
			var view:View = new View(model);
			addChild(view);
			// 開始
			model.chain = chain;
			model.start();
			Wonderfl.capture_delay(30);
		}
	}
}

	import flash.display.BitmapData;
	import flash.geom.Point;
	/**
	 * BitmapData エフェクト用抽象クラス
	 * @author YOSHIDA, Akio
	 */
	class AbstractEffector {
		/*
		 * BitmapData.applyFilter で destPoint として使用する Point オブジェクト
		 */
		protected const ZERO_POINT:Point = new Point(0, 0);
		
		/*
		 * コンストラクタ
		 */
		public function AbstractEffector() {}
		
		/*
		 * 効果の適用
		 * @param	value	効果をかける BitmapData
		 */
		public function applyEffect(value:BitmapData):BitmapData {
			return effect(value);
		}
		
		/*
		 * 効果内容、具体的なコードはサブクラスで定義する
		 * @param	value	効果をかける BitmapData
		 */
		protected function effect(value:BitmapData):BitmapData {
			return value;
		}
	}


	import flash.display.BitmapData;
	/**
	 * Effector のフォーマット
	 * 必ず AbstractEffector を継承する
	 */
	class EffectorFormat extends AbstractEffector {
		// ===== 書き換える場所 ここから =====
		// メンバー変数が必要な場合はここで記述
		// すべての Effector 参照
		// ===== 書き換える場所 ここまで =====

		/*
		 * コンストラクタ
		 */
		public function EffectorFormat() {
			// ===== 書き換える場所 ここから =====
			// このクラス生成時に初期化処理が必要な場合はここで記述。なければ不要
			// EffectorPosterization, EffectorSharp, EffectorSmoothing 参照
			// ===== 書き換える場所 ここまで =====
		}
		/*
		 * エフェクト実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			// ===== 書き換える場所 ここから =====
			// value.applyFilter(value, value.rect, ZERO_POINT, xxxFilter);
			// value.applyFilter を使う場合、第3引数までは固定
			// すべての Effector 参照
			// ===== 書き換える場所 ここまで =====
			return value;
		}
	}

	
	import flash.display.BitmapData;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;
	/**
	 * BlurFilter による平滑化
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorSmoothing extends AbstractEffector {
		// BlurFilter 用変数(強さ)
		public function set strength(value:Number):void {
			blurFilter.blurX = blurFilter.blurY = value;
		}
		// BlurFilter 用変数(質)
		public function set quality(value:int):void {
			blurFilter.quality = value;
		}
		// BlurFilter
		private var blurFilter:BlurFilter;

		/*
		 * コンストラクタ
		 */
		public function EffectorSmoothing() {
			blurFilter = new BlurFilter(2, 2, BitmapFilterQuality.MEDIUM);
		}
		/*
		 * 平滑化実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			value.applyFilter(value, value.rect, ZERO_POINT, blurFilter);
			return value;
		}
	}

	
	import flash.display.BitmapData;
	/**
	 * paletteMap による BitmapData の減色
	 * 参考:「実践画像処理入門」 培風館 内村圭一・上瀧剛 P16 「2.5 濃度値の量子化による減色処理」
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorPosterization extends AbstractEffector {
		/*
		 * 減色の段階(1、および 256 より大きい値以外)
		 */
		public function set degree(value:uint):void {
			value = value < 2   ? 2   : value;
			value = value > 256 ? 256 : value;
			
			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;
			}
		}
		// paletteMap の引数となる各色要素用の Array
		private var rArray:Array = [];
		private var gArray:Array = [];
		private var bArray:Array = [];

		
		/*
		 * コンストラクタ
		 */
		public function EffectorPosterization() {
			degree = 8;		// degree のデフォルト
		}
		
		/*
		 * 減色実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			value.paletteMap(value, value.rect, ZERO_POINT, rArray, gArray, bArray);
			return value;
		}
	}
	
	
	import flash.display.BitmapData;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	/**
	 * モザイク(原画像のブロック内部の画素値の平均値による)
	 * 「OpenGL+GLSLによる画像処理プログラミング」 工学社 酒井幸市 P46 「2.1 モザイク処理」
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorPixelization extends AbstractEffector {
		
		public function set size(value:uint):void {
			_size = value;
			wNum = Math.ceil(width  / _size);	// width / _size が割り切れない場合は切り上げ
			hNum = Math.ceil(height / _size);	// height / _size が割り切れない場合は切り上げ
			pixelizeRect.width  = _size;
			pixelizeRect.height = _size;
			if (pixelizeBitmapData) pixelizeBitmapData.dispose();
			pixelizeBitmapData = new BitmapData(_size, _size);
		}
		private var _size:uint;					// モザイクの1辺の長さ(縦横同じ長さとする)
		
		private var width:Number;				// 対象幅
		private var height:Number;				// 対象高
		private var wNum:uint;					// 横方向のモザイク数
		private var hNum:uint;					// 縦方向のモザイク数
		private var pixelizeRect:Rectangle = new Rectangle();	// モザイク1つ分の Rectangle
		private var pixelizeBitmapData:BitmapData;				// モザイク1つ分の BitmapData
		
		private var matrix:Matrix = new Matrix();	// BitmapData.draw 用

		
		public function EffectorPixelization(width:Number, height:Number) {
			this.width  = width;	// 対象幅を待避
			this.height = height;	// 対象高を待避
			size = 8;				// _size のデフォルト(setter メソッドを発動させる)
		}
		
		override protected function effect(value:BitmapData):BitmapData {
			// モザイクブロックごとの走査
			for (var i:int = 0; i < hNum; i++) {
				for (var j:int = 0; j < wNum; j++) {
					pixelizeRect.x = j * _size;
					pixelizeRect.y = i * _size;
					matrix.tx = -pixelizeRect.x;
					matrix.ty = -pixelizeRect.y;
					pixelizeBitmapData.draw(value, matrix);
					// モザイクの1辺の長さが 16 より大きい場合はヒストグラムを使って、そうでない場合は getPixel を使って平均色を求める
					// 16 ^ 2 = 256
					// 走査ループ回数がヒストグラムでのそれの未満なら getPixel に切り替える
					var color:uint = (_size > 16) ? getAverageColorHistogram(pixelizeBitmapData) : getAverageColor1GetPixel(pixelizeBitmapData);
					value.fillRect(pixelizeRect, color);
				}
			}
			return value;
		}
		
		// 平均色を求める(モザイク1ブロック内のヒストグラムから計算)
		private function getAverageColorHistogram(bitmapData:BitmapData):uint {
			var hist:Vector.<Vector.<Number>> = bitmapData.histogram();
			var rSum:uint = 0;
			var gSum:uint = 0;
			var bSum:uint = 0;
			for (var i:int = 0; i < 256; i++) {
				rSum += i * hist[0][i];
				gSum += i * hist[1][i];
				bSum += i * hist[2][i];
			}
			var numOfPixel:uint = _size * _size;
			var r:uint = rSum / numOfPixel >> 0;
			var g:uint = gSum / numOfPixel >> 0;
			var b:uint = bSum / numOfPixel >> 0;
			return 0xFF << 24 | r << 16 | g << 8 | b;
		}
		
		// 平均色を求める(モザイク1ブロック内のピクセルを走査)
		private function getAverageColor1GetPixel(bitmapData:BitmapData):uint {
			var rSum:uint = 0;
			var gSum:uint = 0;
			var bSum:uint = 0;
			for (var i:int = 0; i < _size; i++) {
				for (var j:int = 0; j < _size; j++) {
					var color:uint = bitmapData.getPixel(j, i);
					var r:uint = (color >> 16) & 0xFF;
					var g:uint = (color >>  8) & 0xFF;
					var b:uint =  color        & 0xFF;
					rSum += r;
					gSum += g;
					bSum += b;
				}
			}
			var numOfPixel:uint = _size * _size;
			r = rSum / numOfPixel >> 0;
			g = gSum / numOfPixel >> 0;
			b = bSum / numOfPixel >> 0;
			return 0xFF << 24 | r << 16 | g << 8 | b;
		}
	}
	
	
	import flash.display.BitmapData;
	import flash.filters.ConvolutionFilter;
	/**
	 * 鮮鋭化
	 * 参考:「実践画像処理入門」 培風館 内村圭一・上瀧剛 P48 「6.2 高域強調フィルタ」
	 * 参考:「OpenGL+GLSLによる画像処理プログラミング」 工学社 酒井幸市 P111 「5.2 鮮鋭化フィルタ」
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorSharp extends AbstractEffector {
		/*
		 * 鮮鋭化の強さ
		 */
		public function set strength(value:Number):void {
			sharpMatrix = [
				0,             -value, 0,
				-value, 1 + 4 * value, -value,
				0,             -value, 0
			];
		}
		// ConvolutionFilter 用マトリクス
		private var sharpMatrix:Array = [];

		/*
		 * コンストラクタ
		 */
		public function EffectorSharp() {
			strength = 1.0;		// strength のデフォルト
		}
		
		/*
		 * 鮮鋭化実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			value.applyFilter(value, value.rect, ZERO_POINT, new ConvolutionFilter(3, 3, sharpMatrix));
			return value;
		}
	}

	
	import flash.display.BitmapData;
	import flash.filters.ColorMatrixFilter;
	/**
	 * 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)
	 */
	class EffectorGrayScale extends AbstractEffector {
		// ColorMatrixFilter 用マトリクス
		private const GRAYSCALE_MATRIX:Array = [
			0.3, 0.6, 0.1, 0, 0,
			0.3, 0.6, 0.1, 0, 0,
			0.3, 0.6, 0.1, 0, 0,
			0,   0,   0,   1, 0
		];
		// ColorMatrixFilter
		private const GRAYSCALE_FILTER:ColorMatrixFilter = new ColorMatrixFilter(GRAYSCALE_MATRIX);
		
		/*
		 * グレイスケール実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			value.applyFilter(value, value.rect, ZERO_POINT, GRAYSCALE_FILTER);
			return value;
		}
	}

	
	import flash.display.BitmapData;
	import flash.filters.ConvolutionFilter;
	/**
	 * ConvolutionFilter による BitmapData の色反転
	 * 参考:http://www40.atwiki.jp/spellbound/pages/231.html
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorNegative extends AbstractEffector {
		// ConvolutionFilter 用マトリクス
		private const NEGATIVE_MATRIX:Array = [-1];
		// ConvolutionFilter
		private const NEGA_FILTER:ConvolutionFilter = new ConvolutionFilter(1, 1, NEGATIVE_MATRIX, 1, 255);

		/*
		 * 色反転実行
		 * @param	value	効果をかける BitmapData
		 */
		override protected function effect(value:BitmapData):BitmapData {
			value.applyFilter(value, value.rect, ZERO_POINT, NEGA_FILTER);
			return value;
		}
	}

	
	import flash.display.BitmapData;
	/**
	 * エフェクタ連鎖(責任の連鎖パターンってこういうの?)
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class ChainEffectors {

		private var effectors:Array = [];
	
		/**
		 * コンストラクタ
		 * @param	model	Model
		 */
		public function ChainEffectors() {
		}
		
		/**
		 * エフェクタの追加
		 * @param	effector	エフェクタ
		 */
		public function addEffector(effector:AbstractEffector):void {
			effectors.push(effector);
		}
		
		/**
		 * エフェクタの適用
		 * @param	value	エフェクタをかける BitmapData
		 */
		public function applyEffect(value:BitmapData):BitmapData {
			var n:uint = effectors.length;
			for(var i:int=0; i < n; i++){
				var effector:AbstractEffector = effectors[i];
				value = effector.applyEffect(value);
			}
			return value;
		}
	}

	
	import flash.display.BitmapData;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.media.Camera;
	import flash.media.Video;
	/**
	 * Web Camera の映像にエフェクトをかける(MVC の Model)
	 * エフェクトロジックは effector クラスとして外部で定義する
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class Model extends EventDispatcher {
		// --------------------------------------------------
		// View へ渡すデータ(プロパティ)
		// --------------------------------------------------
		/**
		 * 加工済みのカメラ画像
		 */
		public function get data():BitmapData { return _data; }
		private var _data:BitmapData;
		
		// --------------------------------------------------
		// 外部から受け取るデータ(プロパティ)
		// --------------------------------------------------
		/**
		 * エフェクタ連鎖
		 */
		public function set chain(value:ChainEffectors):void {
			_chain = value;
		}
		private var _chain:ChainEffectors;
		
		// --------------------------------------------------
		// 外部との通信をおこなうメソッド
		// --------------------------------------------------
		/**
		 * 対 View 用メソッド
		 * このメソッドの終了時にイベントを発行するので、View との通信手段となる
		 * @private
		 */
		private function update():void {
			_data.draw(video);
			_data = _chain.applyEffect(_data);
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		// --------------------------------------------------
		// その他のメソッド
		// --------------------------------------------------
		/**
		 * コンストラクタ
		 * コンストラクタの引数はステージとする。各種データはアクセサーによって取り込むものとする
		 * @param	stage	ステージ
		 */
		private var stage:Stage;

		// カメラが表示するサイズ
		private var cameraWidth:uint;
		private var cameraHeight:uint;
		// カメラ
		private var camera:Camera;
		private var video:Video;
		public function Model(stage:Stage, cw:Number = 0, ch:Number = 0) {
			this.stage = stage;
			this.cameraWidth  = (cw == 0) ? stage.stageWidth  : cw;
			this.cameraHeight = (ch == 0) ? stage.stageHeight : ch;
			
			_data = new BitmapData(cameraWidth, cameraHeight, false);
			
			// カメラ
			camera = Camera.getCamera();
			if (camera) {
				// camera のセットアップ
				camera.setMode(cameraWidth, cameraHeight, stage.frameRate);
				// video のセットアップ
				video = new Video(cameraWidth, cameraHeight);
				video.attachCamera(camera);
			} else {
				throw new Error("カメラがありません。");
			}
		}
		
		/**
		 * 処理開始
		 * Event.ENTER_FRAME を使う場合、このメソッドを設定する。
		 * Controller から通知されるイベントだけで処理する場合、このメソッドは不要。
		 */
		public function start():void {
			stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		/**
		 * イベントハンドラ
		 * @private
		 */
		private function enterFrameHandler(event:Event):void {
			update();
		}
	}

	
	import flash.display.Bitmap;
	import flash.events.Event;
	/**
	 * Web Camera のスクリーン(MVC の View)
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class View extends Bitmap {
		/**
		 * コンストラクタ
		 * @param	model	Model
		 */
		private var model:Model;
		public function View(model:Model) {
			this.model = model;
			this.model.addEventListener(Event.CHANGE, changeHandler);
		}
		
		/**
		 * Model との通信手段
		 * @param	event	発生したイベント
		 */
		private function changeHandler(event:Event):void {
			// Model からデータを受け取り、視覚化
			this.bitmapData = model.data;
		}
	}