// forked from yooKo's 外部SWFアニメから画像の輪郭抽出してPixel表示 feat.chibitami // forked from yooKo's 外部SWFアニメから画像の輪郭抽出してPixel表示 /** * 1.外部SWFから輪郭を取得する。 * 2.Papervision3DのPixcel3Dで描画 * * @author yooKo@serialField * @graphic chibitami * @version 2.0 * Let's Click! * Click毎に3Dエフェクトが切り替わるお~~ * * 2009/10/13 13:49 * swfアニメの素材を変更 * chibitami さんのswfアニメのモーションがかなり見栄えがよくサンプルに * ちょうどよかったので許可を頂いてお借りしました。 * 画像元 * 【CHiBiTAMi.NET/WORKS/】 * http://chibitami.net/works/ * 【Twitter】 * http://twitter.com/chibitami * **/ /* 追記 by clockmaker したこと ・stage.quality ・bitmapDataのlockとunclock やったら効果あるんじゃないかと思うこと ・画像解析の結果を予め配列に格納 ・Papervision3D(Pixels3D)を使わず、2DのBitmapDataに書き込んで、 それをFlash 10の3Dで回転させるとか */ package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.StageQuality; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.geom.ColorTransform; import flash.filters.BitmapFilterQuality; import flash.geom.Point; import flash.net.URLRequest; import flash.system.LoaderContext; //import flash.system.SecurityDomain; import flash.system.Security; import org.papervision3d.core.effects.BitmapLayerEffect; import org.papervision3d.core.effects.BitmapColorEffect; import org.papervision3d.core.effects.utils.BitmapClearMode; import org.papervision3d.core.geom.Pixels; import org.papervision3d.core.geom.renderables.Pixel3D; import org.papervision3d.view.BasicView; import org.papervision3d.view.layer.BitmapEffectLayer; import net.hires.debug.Stats; import org.si.sion.SiONDriver; import org.si.sion.SiONData; import org.si.sion.utils.SiONPresetVoice; [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")] public class Pixcel3DAnime extends BasicView { private const IMAGE_URL:String = "http://selflash.jp/wonderfl/dance_girl.swf"; //private const FILTER_BLUR:BlurFilter = new BlurFilter(8, 8, BitmapFilterQuality.LOW); private const TRANSFORM_COLOR:ColorTransform = new ColorTransform(0, 0, 0, 0, 0, 0, 0, 0); private const CAMERA_DISTANCE:int = -200; private var _loader:Loader; private var _pixels:Pixels; private var _rotateX:Number = 0; private var _rotateY:Number = 0; private var _currentNum:int = 0; private var _bmd:BitmapData; private var _pre:Number; private var _a:Number; private var _y:Number; private var _x:Number; private var _c:uint; private var _vx:Number = 0; private var _xx:Number = 0; private var _vy:Number = 0; private var _yy:Number = 0; private var _driver:SiONDriver = new SiONDriver(); private var _voices:SiONPresetVoice = new SiONPresetVoice(); // ソース画像のどの部分を解析するか。画像をさしかえる時はここを任意の値に //解析する開始位置 private const TRIMMING_LEFT:int = 0; private const TRIMMING_TOP:int = 0; //解析する終わりの位置 private const TRIMMING_RIGHT:int = 280; private const TRIMMING_BOTTOM:int = 280; //===================================================== //除外したい色 //private var EXCLUSION_COLOR:int = 0xFFFFFF; //白で抜きたいよー private var EXCLUSION_COLOR:int = 0x00; //アルファで抜きたいよー // 中央に持ってくる為に使う private const W:Number = (TRIMMING_RIGHT + TRIMMING_LEFT) * .5/*TRIMMING_LEFT + TRIMMING_RIGHT * .5*/; private const H:Number = (TRIMMING_BOTTOM + TRIMMING_TOP) * .5/*TRIMMING_TOP + TRIMMING_BOTTOM * .5*/; //======================================================================== // コンストラクタ //======================================================================== public function Pixcel3DAnime() { super(0, 0, true, true); stage.quality = StageQuality.LOW; /** *crossdomainにやらSecurity.allowDomain()やらまだまだ理解が浅くてわかんなかった、、 * 読み込む事はできるがdraw()ができなくて悩んだ。 * 結局呼び出す子swfの1フレーム目にSecurity.allowDomain("*")と記述した事でおk。 * ※Security.allowDomain("http://www.selflash.jp/")ではダメだった・・この辺はなんでか分かんない * http://cocoasaurus.com/blog/2009/09/as3allowdomain-1.html * ↑のリンク先の情報によると読み込む方と読み込まれる方の両方に Security.allowDomain() を記述しないといけないらしいが * ↓はコメントアウトしても draw() 出来てる。これもなぜだかわかんない、、、 */ //Security.allowDomain("*"); if (!stage) addEventListener(Event.ADDED_TO_STAGE, init); else init(); } //======================================================================== // 画像の読み込み //======================================================================== private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); _loader = new Loader(); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteHandler); _loader.load(new URLRequest(IMAGE_URL), new LoaderContext(true)); } //======================================================================== // ロード完了後の処理 //======================================================================== private function onCompleteHandler(e:Event):void { e.target.removeEventListener(Event.COMPLETE, init); //こいつから毎回色、座標を解析する _bmd = new BitmapData(TRIMMING_RIGHT, TRIMMING_BOTTOM, true, 0xFFFFFF); //左下に表示するためのやつね var bmp:Bitmap = new Bitmap(_bmd, "auto", false); bmp.scaleX = .4; bmp.scaleY = .4; bmp.y = stage.stageHeight - bmp.height; addChild(bmp); camera.z = CAMERA_DISTANCE; var _layer:BitmapEffectLayer = new BitmapEffectLayer(viewport, stage.stageWidth, stage.stageHeight, true, 0, BitmapClearMode.CLEAR_PRE, false); viewport.containerSprite.addLayer(_layer); _layer.addEffect(new BitmapColorEffect(1.2, 1.2, 1.2, .5)); //_layer.drawCommand.blendMode = BlendMode.ADD; //_layer.addEffect(new BitmapLayerEffect(new BlurFilter(8, 8, BitmapFilterQuality.MEDIUM), false)); _pixels = new Pixels(_layer); scene.addChild(_pixels); var loop:SiONData = _driver.compile("t126; %6@0o3l4$cccc; %6@1o3$rcrc; %6@2v8l16$[crccrrcc]; %6@3v8o3$[rc8r8];"); var voices:Array = _voices['valsound.percus']; loop.setVoice(0, voices[0]); loop.setVoice(1, voices[27]); loop.setVoice(2, voices[16]); loop.setVoice(2, voices[21]); _driver.play(loop); addEventListener(Event.ENTER_FRAME, upDate); addEventListener(MouseEvent.MOUSE_DOWN, onDownHandler); startRendering(); addChild(new Stats()); } //======================================================================== // 表示の切り替え // ここ失敗作。。切り替えの仕組みがしょぼすぎる・・・なんか挙動が変、、スマートな方法あったら教えてください、、 //======================================================================== private function onDownHandler(e:MouseEvent = null):void { _currentNum++; //if (_currentNum > 6) { //_currentNum = 0; //} if(_currentNum > 6)_currentNum = 0; _vx = (Math.random() - .5) * 200; _vy = (Math.random() - .5) * 200; } //======================================================================== // 常に行う処理 //======================================================================== private function upDate(e:Event):void { _rotateX += (- viewport.containerSprite.mouseX - _rotateX) * 0.1; _rotateY += (- viewport.containerSprite.mouseY - 170 - _rotateY) * 0.1; _pixels.rotationY = _rotateX; _pixels.rotationX = _rotateY; //_bmd.applyFilter(_bmd, _bmd.rect, new Point(), FILTER_BLUR); //_bmdの中身を毎回消すには↓のどっちかだけどnew BitmapData()するとやっぱり重い、、 _bmd.colorTransform(_bmd.rect, TRANSFORM_COLOR); //_bmd = new BitmapData(TRIMMING_RIGHT, TRIMMING_BOTTOM, true, 0x00); _bmd.draw(_loader); _pixels.removeAllpixels(); /** エフェクトの切り替え * ================================================================= * 基本的には createBody()で全体,createFrame()で枠組みのみを生成します。 * 引数の説明 * 1つ目はレイヤーのz座標。 * 2つ目はドットの細かさ。 * 3つ目の色を指定しないという事はBitmapDataから取得した色をそのまま使うという事です。 * 引数を調整したりcreateBody(),createFrame()を増やしたりして調整してください。 * ================================================================== */ switch (_currentNum) { // ノーマル ////////////////////////////////////// case 0: // swfから取得した色で全体を表示する1層のレイヤー画像を作成 createBody(0, 2); break; // 3D ////////////////////////////////////////// case 1: // 指定した色で表示をする5層のレイヤーからなる厚みのある画像を作成。bodyでframeをサンドイッチ createBody(-9, 4, 0xFFCCFF33); createFrame(-6, 5, 0xFFCCFF33); createFrame(-3, 5, 0xFFCCFF33); createFrame(0, 5, 0xFFCCFF33); createFrame(3, 5, 0xFFCCFF33); createFrame(6, 5, 0xFFCCFF33); createBody(9, 4, 0xFFCCFF33); break; // シルエット ///////////////////////////////////// case 2: // 指定した色で全体を表示する1層のレイヤー画像を作成。 createBody(0, 3, 0xFFCCFF00); break; // フレーム ////////////////////////////////////// case 3: // 指定した色で枠組みを表示する1層のレイヤー画像を作成。 createFrame(0, 2, 0xFFCCFF99); break; // 地震エフェクト? ///////////////////////////////// case 4: // 指定した色で枠組みを表示する1層のレイヤー画像を作成。 createBody(0, 3, 0, ((Math.random() - .5) * 50)); break; // ディスコエフェクト ////////////////////////////////// case 5: // 指定した色で枠組みを表示する1層のレイヤー画像を作成。 createBody(0, 3, ((Math.random() - .7) * 5), ((Math.random() - .5) * 50)); break; //ノイズエフェクト ///////////////////////////////////// case 6: // 指定した色で枠組みを表示する1層のレイヤー画像を作成。 createBody(0, 3, 0xFF33CC00, 0, true); break; }; _xx += (_vx - _xx ) * .1; _yy += (_vy - _yy ) * .1; } //======================================================================== // 中身を作成するメソッド // 呼び出されたら白以外全部解析する // ※ 引数に色の指定がなければ抽出した色を使う //======================================================================== private function createBody(depth:Number = 0, distance:Number = 2, color:Number = NaN, disco:Number = 0, noise:Boolean = false):void { _bmd.lock(); for ( _y = TRIMMING_TOP; _y < TRIMMING_BOTTOM; _y += distance ) { for ( _x = TRIMMING_LEFT; _x < TRIMMING_RIGHT; _x += distance ) { _c = _bmd.getPixel( _x, _y ); if (_c != EXCLUSION_COLOR) { _c = (color)?color:rgb2argb(_c, 1); if (noise) { depth = depth + ((Math.random() - .5) * 2); }; _pixels.addPixel3D(new Pixel3D(_c, _x - W, _y - H, depth + disco)); } } } _bmd.unlock(); } //======================================================================== // 枠組みを作成するメソッド // 呼び出されたらキャラクターの外枠だけ解析する // ※ 引数に色の指定がなければ抽出した色を使う //======================================================================== private function createFrame(depth:Number = -4, distance:Number = 2, color:Number = NaN, noise:Number = 0):void { _bmd.lock(); for ( _y = TRIMMING_TOP; _y < TRIMMING_BOTTOM; _y += distance ) { for ( _x = TRIMMING_LEFT; _x < TRIMMING_RIGHT; _x += distance ) { _c = _bmd.getPixel( _x, _y ); if (_c != EXCLUSION_COLOR) { _a = (_y == 0)?0:_x - W - _pre; if (_a > distance || _a < - distance) { _c = (color)?color:rgb2argb(_c, 1); _pixels.addPixel3D(new Pixel3D(_c, _x - W, _y - H, depth)); } _pre = _x - W; } } } for ( _x = TRIMMING_LEFT; _x < TRIMMING_RIGHT; _x += distance ) { for ( _y = TRIMMING_TOP; _y < TRIMMING_BOTTOM; _y += distance ) { _c = _bmd.getPixel( _x, _y ); if (_c != EXCLUSION_COLOR) { _a = (_x == 0)?0:_y - H - _pre; if(_a > distance || _a < - distance) { _c = (color)?color:rgb2argb(_c, 1); _pixels.addPixel3D(new Pixel3D(_c, _x - W, _y - H, depth)); } _pre = _y - H; } } } for ( _y = TRIMMING_BOTTOM; _y > TRIMMING_TOP; _y -= distance ) { for ( _x = TRIMMING_RIGHT; _x > TRIMMING_LEFT; _x -= distance ) { _c = _bmd.getPixel( _x, _y ); if (_c != EXCLUSION_COLOR) { _a = (_y == TRIMMING_TOP + TRIMMING_BOTTOM)?0:_x - W - _pre; if(_a > distance || _a < - distance) { _c = (color)?color:rgb2argb(_c, 1); _pixels.addPixel3D(new Pixel3D(_c, _x - W, _y - H, depth)); } _pre = _x - W; } } } for ( _x = TRIMMING_RIGHT; _x > TRIMMING_LEFT; _x -= distance ) { for ( _y = TRIMMING_BOTTOM; _y > TRIMMING_TOP; _y -= distance ) { _c = _bmd.getPixel( _x, _y ); if (_c != EXCLUSION_COLOR) { _a = (_x == TRIMMING_LEFT + TRIMMING_RIGHT)?0:_y - H - _pre; if(_a > distance || _a < - distance) { _c = (color)?color:rgb2argb(_c, 1); _pixels.addPixel3D(new Pixel3D(_c, _x - W, _y - H, depth)); } _pre = _y - H; } } } _bmd.unlock(); } //======================================================================== // RGBをARGBに変換する //======================================================================== private function rgb2argb(rgb:uint, alpha:Number):uint { return ((alpha * 0xff) << 24) + rgb; } } } forked from: プチ高速化:外部SWFアニメから画像の輪郭抽出してPixel表示 feat.chibitami