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

[WebCam] 誰でもスーラ -Anyone Seurat-

誰でもスーラ -Anyone Seurat-
点描派風ピクセレート・ウェブカム版
ネタ元:Beyond Interaction -メディアアートのための openFrameworks プログラミング入門 P169
http://www.amazon.co.jp/gp/product/4861006708?ie=UTF8&tag=laxcomplex-22&linkCode=as2&camp=247&creative=1211&creativeASIN=4861006708
参考:http://wonderfl.net/code/2bfaa6e6b54bc46b3aa9b0210103f3812aa00376
@author Aquioux(Yoshida, Akio)
Get Adobe Flash player
by Aquioux 26 Mar 2010
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/sJ7a
 */

package {
	import flash.display.Sprite;
	import net.hires.debug.Stats;
	[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
	/**
	 * 誰でもスーラ -Anyone Seurat-
	 * 点描派風ピクセレート・ウェブカム版
	 * ネタ元:Beyond Interaction -メディアアートのための openFrameworks プログラミング入門 P169
	 * http://www.amazon.co.jp/gp/product/4861006708?ie=UTF8&tag=laxcomplex-22&linkCode=as2&camp=247&creative=1211&creativeASIN=4861006708
	 * 参考:http://wonderfl.net/code/2bfaa6e6b54bc46b3aa9b0210103f3812aa00376
	 * @author Aquioux(Yoshida, Akio)
	 */
	public class Main extends Sprite {
		
		public function Main():void {
			Wonderfl.capture_delay(20);
			
			// model
			try {
				var model:Model = new Model(stage);
			} catch (err:Error) {
				trace(err.message);
				return;
			}
			
			// view
			var view:View = new View(model);
			addChild(view);
			
			addChild(new Stats());
		}
	}
}


	import flash.display.BitmapData;
	import flash.display.Stage;
	import flash.events.ActivityEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.geom.Matrix;
	import flash.media.Camera;
	import flash.media.Video;
	/**
	 * Model
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class Model extends EventDispatcher {
		// ---------- パブリックプロパティ ----------
		//
		// View へ渡すデータ
		public function get data():BitmapData { return _data; }
		private var _data:BitmapData;

		
		// ---------- ローカルプロパティ ----------
		//
		private var stage_:Stage;
		private var cameraWidth_:uint;
		private var cameraHeight_:uint;
		private var camera_:Camera;
		private var video_:Video;
		private var matrix_:Matrix;
		

		// ---------- パブリックメソッド ----------
		//
		/**
		 * コンストラクタ
		 * @param	stage	ステージ
		 * @param	cw		カメラ幅(省略時はステージ幅)
		 * @param	ch		カメラ高(省略時はステージ高)
		 */
		public function Model(stage:Stage, cw:uint = 0, ch:uint = 0) {
			// 引数の処理
			stage_ = stage;
			var w:uint = stage_.stageWidth;
			var h:uint = stage_.stageHeight;
			cameraWidth_  = (cw == 0) ? w : cw;
			cameraHeight_ = (ch == 0) ? h : ch;

			// View へ渡すデータの生成
			_data = new BitmapData(w, h, false, 0x000000);
			
			// 鏡像になるよう、Matrix で左右反転
			var tx:uint = (w - cameraWidth_)  / 2 + cameraWidth_;
			var ty:uint = (h - cameraHeight_) / 2;
			matrix_ = new Matrix( -1, 0, 0, 1, tx, ty);
			
			// カメラ準備
			camera_ = Camera.getCamera();
			if (camera_) {
				// camera のセットアップ
				camera_.setMode(cameraWidth_, cameraHeight_, stage_.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 {
			camera_.removeEventListener(ActivityEvent.ACTIVITY, arguments.callee);
			stage_.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		// ENTER_FRAME イベントハンドラ
		private function enterFrameHandler(event:Event):void {
			_data.draw(video_, matrix_);
			dispatchEvent(new Event(Event.CHANGE));
		}
	}


	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;
	/**
	 * View
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class View extends Bitmap {
		// ---------- パブリックプロパティ ----------
		//
		// Model の参照
		public function set model(value:Model):void { _model = value; }
		private var _model:Model;
		
		// Controller の参照
		//public function set controller(value:Controller):void { _controller = value; }
		//private var _controller:Controller;
		
		
		// ---------- ローカルプロパティ ----------
		//
		// 各エフェクタ
		private var smooth:Smooth;		// 平滑化のためのエフェクタ
		//private var smooth2:Smooth;		// 最後にぼかすためのエフェクタ
		private var tone:HalftoneColor;	// ハーフトーン化
		

		// ---------- パブリックメソッド ----------
		//
		/**
		 * コンストラクタ
		 * @param	model	Model
		 */
		public function View(model:Model) {
			_model = model;
			_model.addEventListener(Event.CHANGE, changeHandler);
			
			smooth = new Smooth();		// 平滑化
			smooth.strength = 2;
			tone = new HalftoneColor();	// ハーフトーン
			tone.size = 20;
			
			filters = [new BlurFilter(8, 8, BitmapFilterQuality.MEDIUM)];
		}
		

		// ---------- ローカルメソッド ----------
		//
		// Model から Event.CHANGE が発行されたときの処理
		private function changeHandler(e:Event):void {
			// Model からデータを受け取り、視覚化
			var bmd:BitmapData = _model.data;
			smooth.applyEffect(bmd);	// 平滑化
			tone.applyEffect(bmd);		// ハーフトーン
			bitmapData = bmd;
		}
	}


	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.geom.Rectangle;
	/**
	 * ハーフトーン(カラー)
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class HalftoneColor implements IEffector {
		// ---------- パブリックプロパティ ----------
		//
		public function set size(value:uint):void {
			_size = value;

			wNum_ = Math.ceil(width_  / _size);	// width / _size が割り切れない場合は切り上げ
			hNum_ = Math.ceil(height_ / _size);	// height / _size が割り切れない場合は切り上げ
			
			if (blockBmd_) blockBmd_.dispose();
			blockBmd_ = new BitmapData(_size, _size);
			
			blockRect_.width = blockRect_.height = _size;
			
			blockPixels_ = _size * _size;
		}
		private var _size:uint = 16;				// モザイクの1辺の長さ(縦横同じ長さとする)
		

		// ---------- ローカルプロパティ ----------
		//
		private var width_:Number;			// 対象幅
		private var height_:Number;			// 対象高

		private var wNum_:uint;				// 横方向のモザイク数
		private var hNum_:uint;				// 縦方向のモザイク数
		
		private var blockBmd_:BitmapData;	// モザイク1つ分の BitmapData
		private var blockRect_:Rectangle;// モザイク1つ分の Rectangle(モザイク領域読込時とトーン描画時で使い回す)
		private var blockPixels_:uint;		// モザイク1つ分の pixel 数
		
		// バッファ
		static private var rBmd_:BitmapData;	// for RED Channel
		static private var gBmd_:BitmapData;	// for GREEN Channel
		static private var bBmd_:BitmapData;	// for BLUE Channel
		static private var bufferBmds_:Array;

		static private var totalRect_:Rectangle;// 全体の Rectangle

		
		// ---------- パブリックメソッド ----------
		//
		/*
		 * コンストラクタ
		 */
		public function HalftoneColor(width:Number = 0.0, height:Number = 0.0) {
			width_  = width;	// 対象幅を待避
			height_ = height;	// 対象高を待避
			blockRect_ = new Rectangle();
			if (width_ != 0.0 && height_ != 0.0) init(width, height);
		}
		
		/*
		 * 効果適用
		 * @param	value	効果対象 BitmapData
		 */
		public function applyEffect(value:BitmapData):BitmapData {
			// 2回目以降
			if (rBmd_){
				rBmd_.fillRect(totalRect_, 0x00000000);
				gBmd_.fillRect(totalRect_, 0x00000000);
				bBmd_.fillRect(totalRect_, 0x00000000);
			}
			// 初回
			if (width_ == 0.0 || height_ == 0.0) init(value.width, value.height);

			var saveBmd:BitmapData = value.clone();	// カメラ画像を待避
			value.fillRect(value.rect, 0xFF000000);	// 塗りつぶす

			// モザイクブロックごとの走査
			value.lock();
			for (var i:int = 0; i < hNum_; i++) {
				for (var j:int = 0; j < wNum_; j++) {
					var px:Number = j * _size;
					var py:Number = i * _size;
					// モザイク領域読込用としての blockRect_ 設定
					blockRect_.x = px;
					blockRect_.y = py;
					blockRect_.width  = blockRect_.height = _size;
					// モザイク領域を全体から切り取る
					blockBmd_.copyPixels(saveBmd, blockRect_, EffectorUtils.ZERO_POINT);
					// モザイク領域の各カラーチャンネルの平均輝度取得
					var brightness:Vector.<uint> = getAverageBrightness(blockBmd_.histogram());
					for (var k:int = 0; k < 3; k++) {
						// 描画サイズの設定
						var blockSize:Number = _size * (brightness[k] / 255) * 0.9;	// 90% 補正
						// 描画開始位置オフセット計算
						var offset:Number = (_size - blockSize) / 2;
						// トーン描画用としての blockRect_ 設定
						blockRect_.x = px + offset + Math.random() - 0.5;
						blockRect_.y = py + offset + Math.random() - 0.5;
						blockRect_.width = blockRect_.height = blockSize;
						// バッファに描画
						bufferBmds_[k].fillRect(blockRect_, 0xFFFFFFFF);
					}
				}
			}
			value.copyChannel(rBmd_, totalRect_, EffectorUtils.ZERO_POINT, BitmapDataChannel.RED, BitmapDataChannel.RED);
			value.copyChannel(gBmd_, totalRect_, EffectorUtils.ZERO_POINT, BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);
			value.copyChannel(bBmd_, totalRect_, EffectorUtils.ZERO_POINT, BitmapDataChannel.BLUE, BitmapDataChannel.BLUE);
			value.unlock();
			return value;
		}
		

		// ---------- ローカルメソッド ----------
		//
		// 初期化
		private function init(width:Number, height:Number):void {
			width_  = width;	// 対象幅を待避
			height_ = height;	// 対象高を待避
			size    = _size;	// _size の設定

			rBmd_ = new BitmapData(width_, height_, true, 0x00000000);
			gBmd_ = rBmd_.clone();
			bBmd_ = rBmd_.clone();
			bufferBmds_ = [rBmd_, gBmd_, bBmd_];
			totalRect_ = new Rectangle(0, 0, width_, height_);
		}

		// 平均輝度を求める
		private function getAverageBrightness(hist:Vector.<Vector.<Number>>):Vector.<uint> {
			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 r:uint = rSum / blockPixels_ >> 0;
			var g:uint = gSum / blockPixels_ >> 0;
			var b:uint = bSum / blockPixels_ >> 0;
			
			return Vector.<uint>([r, g, b]);
		}
	}


	import flash.display.BitmapData;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;
	/**
	 * BlurFilter による平滑化
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class Smooth implements IEffector {
		// ---------- パブリックプロパティ ----------
		//
		/*
		 * ぼかしの強さ
		 * @param	value	数値
		 */
		public function set strength(value:Number):void {
			blurFilter_.blurX = blurFilter_.blurY = value;
		}
		/*
		 * ぼかしの質
		 * @param	value	数値
		 */
		public function set quality(value:int):void {
			blurFilter_.quality = value;
		}


		// ---------- ローカルプロパティ ----------
		//
		// ブラーフィルタ
		private var blurFilter_:BlurFilter;


		// ---------- パブリックメソッド ----------
		//
		/*
		 * コンストラクタ
		 */
		public function Smooth() {
			blurFilter_ = new BlurFilter(2, 2, BitmapFilterQuality.MEDIUM);
		}
		
		/*
		 * 効果適用
		 * @param	value	効果対象 BitmapData
		 */
		public function applyEffect(value:BitmapData):BitmapData {
			value.applyFilter(value, value.rect, EffectorUtils.ZERO_POINT, blurFilter_);
			return value;
		}
	}


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


	import flash.geom.Point;
	/**
	 * bitmapDataEffector パッケージ内のクラスで共通に使う定数など
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class EffectorUtils {
		// ---------- パブリックプロパティ ----------
		//
		// BitmapData が備える各種メソッドの destPoint 用
		static public const ZERO_POINT:Point = new Point(0, 0);
	}