2009年2月16日 (月)

RED5をやってみた。

6年位前にFlash Communication Server MXでマルチユーザーの実験をしたことはあったけど、未だに案件でそういうコンテンツを作る機会が無く。。
企画の段階で話が出たりすることもあるけど、結局サーバの問題でNGになることが多いなあ。
(クライアントのサーバをいじれない、他に専用サーバを用意する費用がない、等)

でも今年やっと仕事でマルチユーザのコンテンツができるかもしれない!
その場合はFlash Media Serverという選択肢はあるんだけど、やっぱり高いんで無料でできるRED5を試してみました。

インストールやら設定なんかは他のサイトを参考にやってみました。
ただサーバサイドのプログラムの作成のとこで、参考サイト通りにやってもできずに悩みました。。

<参考>
http://coelacanth.heteml.jp/site/flash_red5/article_4
http://www.sonicjam.co.jp/soniclabs/?p=46
http://www.thinkit.co.jp/article/152/3/3.html

このへんで書かれてるのはスクリプトを書くところまでだけど、実際はjar形式でエクスポートし、「lib」フォルダに格納しなければいけませんでした。
バージョンが違うのかな?それかなんか設定がおかしい?まあ動いたからいいや。

以下、参考サイトに書いてあることだけど個人的なまとめ。
サーバーサイドのJavaです。

クライアントからサーバサイドの関数を呼ぶと引数で「IConnection conn」が渡される。
これを使って呼び出し元のクライアントの関数を実行できる。

if (conn instanceof IServiceCapableConnection) {
	IServiceCapableConnection sc = (IServiceCapableConnection) conn;
	sc.invoke("recvID", new Object[]{id, msg});
}

接続しているクライアント全部の関数を実行するのは下記のとおり。

IScope scope = Red5.getConnectionLocal().getScope();
Set roomClients = scope.getClients();
Iterator iter = roomClients.iterator();
while ( iter.hasNext() ) {
	IClient client = (IClient) iter.next();
	Set connset = client.getConnections();
	Iterator itcon = connset.iterator();
	while (itcon.hasNext()) {
		conn = itcon.next();
		if (conn instanceof IServiceCapableConnection) {
			IServiceCapableConnection sc = (IServiceCapableConnection)conn;
			sc.invoke("recv", new Object[]{getClients().size()});
		}
	}
}

誰かが接続してきた時に呼ばれる関数。
この中で接続者全員に現在の接続者数を渡してあげればリアルタイムで接続人数を更新できる。
接続人数は「getClients().size()」で取得できる。
ただし、この中ではまだ新しく接続した人の分はプラスされてないので+1してから返すといい。

public boolean appConnect( IConnection conn , Object[] params ){}

誰かが切断した時に呼ばれる関数。
この中で接続者全員に現在の接続者数を渡してあげればリアルタイムで接続人数を更新できる。
接続人数は「getClients().size()」で取得できる。
ただし、この中ではまだ切断した人の分はマイナスされてないので-1してから返すといい。

public void appDisconnect(IConnection conn) {}

MySQLを使う時は以下の通り。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

Statement stmt = null;
try {
	Class.forName("com.mysql.jdbc.Driver");
	Connection db_conn = DriverManager.getConnection("jdbc:mysql://localhost/dbname", "username", "password");
	stmt = db_conn.createStatement();
	ResultSet rs = stmt.executeQuery("SELECT * FROM id");
	while(rs.next()) {
		//1行ずつ処理するのかな
		//取得するのがStringなのかIntなのか等で関数が変わる
		id = rs.getInt("num");
	}
	stmt.close();
	stmt = null;
	db_conn.close();
	db_conn = null;
	updateID(id+1);
} catch (SQLException ex) {
	//log.debug("SQLException: " + ex.getMessage());
	//log.debug("SQLState: " + ex.getSQLState());
	//log.debug("VendorError: " + ex.getErrorCode());
} catch (Exception e2) {
	//log.debug("Exception: " + e2.getMessage());
}

UPDATEとかINSERTのときは↓こんな感じで。

import java.sql.PreparedStatement;

PreparedStatement updateNum = db_conn.prepareStatement("UPDATE id SET num = ? ");
//SQL文の中の「?」に対して何を指定するか
//第一引数は「何番目の?」に対してか。
//第2引数は「その?に何を入れるか。
updateNum.setInt(1, id);
updateNum.executeUpdate();

てか今年初だ。今年はがんばります。。

| | コメント (0) | トラックバック (0)

2008年11月13日 (木)

PixelBenderで輪郭抽出&輪郭に雪を積もらせる

最近カメラで取得した映像を使って遊んでます。
映像の中の人とかに雪が積もるといいなーと思ってやってみました。

境界線が分かればなんとかなるので、↓こちらを参考にやってみました。
http://web.sfc.keio.ac.jp/~shokai/archives/2007/05/proce55ing-webcam-edge-detect.html

●まずはActionScriptで

元ソースはProcessingなので、ASに書き直す必要があります。
そのままいけないので試行錯誤した結果が下記のような感じです。
(※省略してます)

考え方としては、まず画像全体の色を4段階にして処理をしやすくし、それから境界線を調べていくようです。

//RGBの各チャンネルを3つのBitmapDataのBLUEチャンネルにコピー
bd1Red.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.RED,BitmapDataChannnel.BLUE);
bd1Blue.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.GREEN,BitmapDataChannnel.BLUE);
bd1Green.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.BLUE,BitmapDataChannnel.BLUE);


//4段階に量子化
//各座標の色を取得し、0x00~0xFFまでの値にし、その中でさらに4段階化する。
//0x00~0xFFまでの値にしやすいように全部BLUEチャンネルにコピー
//その状態でgetPixelしたら0x00~0xFF(0x000000~0x0000FF)の範囲で取得できるかと思いきや・・・
//他のチャンネルも同じ値になるみたい。
//仕方なくgetPixelしてから0xFFFF00で割った余りを使って0x00~0xFFの範囲にする
//一度64で割ってから小数点以下を切り捨てて、また64倍することで4段階の色に分かれる
//(0~255の256段階なので4段階にするには4で割った64で割れば0~3の4段階になる)
for(var yy:int = 0; yy < 240; yy++){
	quants_pixels[yy] = [];
	for(var xx:int = 0; xx < 320; xx++){
		var red:Number = bd1Red.getPixel(xx,yy) % 0xFFFF00;
		var blue:Number = bd1Blue.getPixel(xx,yy) % 0xFFFF00;
		var green:Number = bd1Green.getPixel(xx,yy) % 0xFFFF00;
		var level:Number = Math.floor((red + blue + green)/3/64) * 64;
		quants_pixels[yy][xx] = level; //draw each pixel to the screen
	}
}

// 周囲(4ピクセル)の平均値と比較する
//各ピクセルを上下左右の平均と比べて、それより大きいか小さいかで境界線かどうかを判定するようです
for(yy=1; yy<240-1; yy++) {
	for(xx=1; xx<320-1; xx++) {
		//bd2.setPixel(xx,yy,quants_pixels[yy][xx]);
		var around:Number = (quants_pixels[yy - 1][xx] + quants_pixels[yy][xx - 1] + quants_pixels[yy][xx + 1] + quants_pixels[yy + 1][xx]) / 4;
		if (around < quants_pixels[yy][xx]) {
			bd3.setPixel(xx, yy, 0x000000);
		} else {
			bd3.setPixel(xx, yy, 0xFFFFFF);
		}
	}
}

以上のような考え方でやってます。
後半は元のやつとだいたい同じなのでいいと思うけど、前半の処理はかなり効率が悪そう。。
でもとりあえずは境界線を取得できたので、これで雪を降らそうと思ったけど、処理がかなり重いことに気付きました。。

●PixelBenderで

処理を軽くしないといけないんだけどいい方法も思いつかず、検索するのも面倒だなあと思ってたときに「PixelBender使えばいいんじゃない??」って思いついて試しにやってみました。
考え方は一緒で、色を4段階にして、その後に各ピクセルごとに境界線の判定をします。
この2つを1つのPixelBenderの処理でやりたかったけど、PixelBenderも分け分からないので2つに分けました。
でもこれで処理は全然軽くなりましたー。といってもまだ重いけど、ASだけに比べれば全然速い。
考え方は同じでやってるので、ASでやった時とPixelBenderでやった時で特に違いはなく、ただ処理が速くなりました。

これをもっとキレイにして忘年会で使おうと思います。

↓カメラ映像の境界線に雪が積もります。クリックで画面切り替わります。
雪の表現はこちらを参考にしました。


<PixelBender>


kernel FourColor
<	namespace:			"";
	vendor:				"";
	version:			1;
	description:		"";
>
{
	input image4 source;
	output pixel4 result;
	void evaluatePixel()
	{
		pixel4 color = sampleLinear( source, outCoord() );
		float b1 = (color.r + color.g + color.b) / 3.0;
		
		float r = 0.0;
		float g = 0.0;
		float b2 = floor(b1 * 100.0 / 25.0);
		float b = b2 * 25.0 / 100.0;
	
		result = pixel4(r,g,b,1.0);
				
	}
}

<PixelBender>


kernel OutLine
<	namespace:			"";
	vendor:				"";
	version:			1;
	description:		"";
>
{
	input image4 source;
	output pixel4 result;
	void evaluatePixel()
	{
		//ある座標
		float2 xy = outCoord();
		//その座標の上下左右の座標
		float2 xy1 = float2(xy.x, xy.y-1.0);
		float2 xy2 = float2(xy.x-1.0, xy.y);
		float2 xy3 = float2(xy.x+1.0, xy.y);
		float2 xy4 = float2(xy.x, xy.y+1.0);
		//それぞれの色を取得
		pixel4 cl = sampleLinear(source, xy);
		pixel4 cl1 = sampleLinear(source, xy1);
		pixel4 cl2 = sampleLinear(source, xy2);
		pixel4 cl3 = sampleLinear(source, xy3);
		pixel4 cl4 = sampleLinear(source, xy4);
		//各色のBlueチャンネルの色を取得(渡される画像は青のチャンネルしかないはず)
		pixel1 b = cl.b;
		pixel1 b1 = cl1.b;
		pixel1 b2 = cl2.b;
		pixel1 b3 = cl3.b;
		pixel1 b4 = cl4.b;
		
		pixel4 aaa;
		//上下左右の色の平均値よりも大きいか小さいかで境界線を判定
		if ((b1+b2+b3+b4)/4.0 < b) {
			aaa = pixel4(0.0,0.0,0.0,1.0);
		} else {
			aaa = pixel4(1.0,1.0,1.0,1.0);
		}
		
		result = aaa;
	}
}

<ActionScript>

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.filters.BlurFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.display.BlendMode;
	import flash.utils.ByteArray;
	[SWF(width=640,height=480,backgroundColor=0xFFFFFF,frameRate=120)]
	public class Snow extends Sprite {
		//青の4段階色に変換するためのPixelBender
		[Embed(source = "test09.pbj", mimeType = "application/octet-stream")]
		private var shaderData:Class;
		//↑の境界線にするためのPixelBender
		[Embed(source = "test10.pbj", mimeType = "application/octet-stream")]
		private var shaderData2:Class;
				
		private var shader:Shader;
		private var shaderFilter:ShaderFilter;
		private var shader2:Shader;
		private var shaderFilter2:ShaderFilter;

		private var particleList:Array = [];
		
		private var my_cam:Camera;
		private var video:Video;
		private var bd1:BitmapData;
		private var bd2:BitmapData;
		private var bd3:BitmapData;
		private var bdSnow:BitmapData;
		private var bdBlack:BitmapData;
		private var bdSmall:BitmapData;
		private var bmBlack:Bitmap;
		private var pt:Point;
		private var cl:ColorTransform;
		private var mtSmall:Matrix;
		private var mtBig:Matrix;
		public function Snow() {
			addEventListener(Event.ADDED_TO_STAGE, added);
		}
		private function added(e:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, added);
			init();
		}
		private function init():void {
			//PixelBenderのやつをFilterとして使うための処理
			shader = new Shader(ByteArray(new shaderData()));
			shaderFilter = new ShaderFilter(shader);
			shader2 = new Shader(ByteArray(new shaderData2()));
			shaderFilter2 = new ShaderFilter(shader2);

			pt = new Point();
			cl = new ColorTransform();
			
			//カメラ
			my_cam = Camera.getCamera();
			my_cam.setMode(320, 240, 30, false);
			//カメラの映像の表示
			//デフォルトで全画面、ステージクリックで裏の処理過程のBitmapDataが見れるようにサイズ変更
			video = new Video();
			video.attachCamera(my_cam);
			video.width = 640;
			video.height = 480;
			video.x = 0;
			video.y = 0;
			stage.addEventListener(MouseEvent.CLICK, clickVideo);
			function clickVideo(e:MouseEvent):void {
				if (video.width > 500) {
					video.width = 320;
					video.height = 240;
					bmBlack.alpha = 0.0;
				} else {
					video.width = 640;
					video.height = 480;
					bmBlack.alpha = 0.5;
				}
			}

			//カメラの映像をBitmapData化してBlurを少しかけた状態を出力する用
			bd1 = new BitmapData(320, 240, false);
			var bm1:Bitmap = new Bitmap(bd1);
			bm1.x = 320;
			bm1.y = 0;
			addChild(bm1);
			
			//PixelBenderを使って青の4段階色に変換した状態を出力する用
			bd2 = new BitmapData(320, 240, false);
			var bm2:Bitmap = new Bitmap(bd2);
			bm2.x = 0;
			bm2.y = 240;
			addChild(bm2);

			//PixelBenderを使って境界線の状態にしたのを出力する用
			bd3 = new BitmapData(320, 240, false);
			var bm3:Bitmap = new Bitmap(bd3);
			bm3.x = 320;
			bm3.y = 240;
			addChild(bm3);

			//カメラの映像は上のレイヤーにしたいのでここでaddChild
			addChild(video);
			
			//雪用のBitmapDataを毎回黒で塗りなおすので、それ用
			bdBlack = new BitmapData(640, 480, false, 0x000000);
			//カメラ映像は少し暗めにすると雪が見やすくなるので↑を使いまわしてアルファかけて配置
			bmBlack = new Bitmap(bdBlack);
			addChild(bmBlack);
			bmBlack.blendMode = BlendMode.MULTIPLY;
			bmBlack.alpha = 0.5;
			
			//雪を降らす用のBitmapData
			bdSnow = new BitmapData(640, 480, false, 0x000000);
			var bmSnow:Bitmap = new Bitmap(bdSnow);
			addChild(bmSnow);
			//BitmapDataの背景色(黒)を消すためにBlendModeを使う。
			//ScreenでもいいかもしれないけどADDのほうが明るくなるので。
			bmSnow.blendMode = BlendMode.ADD;
			
			//雪で使うのを作っとく
			bdSmall = new BitmapData(320, 240, false, 0x000000);
			mtSmall = new Matrix(0.5, 0, 0, 0.5, 0, 0);
			mtBig = new Matrix(2, 0, 0, 2, 0, 0);
			
			addEventListener(Event.ENTER_FRAME, loop);
			
			
		}
		
		private function loop(e:Event):void {
			//カメラの映像をdraw
			bd1.draw(video);
			//境界線が複雑になり過ぎる場合はブラーかける。かけすぎると境界線が取得しにくくなる。
			//bd1.applyFilter(bd1, bd1.rect, pt, new BlurFilter(2, 2, 1));
			
			//PixelBenderを使って青の4段階色に変換
			bd2.draw(bd1);
			bd2.applyFilter(bd2, bd2.rect, pt, shaderFilter);
			
			//PixelBenderを使って境界線に変換
			bd3.draw(bd2);
			bd3.applyFilter(bd3, bd3.rect, pt, shaderFilter2);
			//ブラーをかけて境界線を太くする
			//これもPixelBenderでやっちゃうほうが速いかも
			bd3.applyFilter(bd3, bd3.rect, pt, new BlurFilter(2, 2, 1));
			
			//雪のオブジェクト(Particle)をaddし、配列particleListに入れてく
			addParticle();
			addParticle();
			addParticle();
			addParticle();
			addParticle();
			
			//雪の表示用BitmapDataを黒く塗りつぶす
			bdSnow.draw(bdBlack);
			
			//particleListに入ってる雪のオブジェクトを1つずつ座標更新、表示していく
			for (var i:int = particleList.length - 1; i >= 0; i--) {
				//雪オブジェクトを取り出す
				var tw:Particle = Particle(particleList[i]);
				//境界線のBitmapData(bd3)の中で、雪オブジェクトと同じ座標の色を取得
				if (tw.y < 4 || tw.y > 476 || bd3.getPixel(Math.round(tw.x/2), Math.round(tw.y/2)) == 0xFFFFFF) {
					//それが白だったら何もない
					tw.update(false);
				} else {
					//白じゃなかったら境界線上
					//元は白or黒だけど、ブラーかけたのでグレーの部分も境界線となるので「白以外は境界線」とする
					tw.update(true);
				}
				
				//雪オブジェクトと同じ座標のピクセルを白で塗りつぶす
				bdSnow.setPixel(tw.x, tw.y, 0xFFFFFF);
				
				//雪オブジェクトが画面から消えてたら配列から削除
				if (tw.del) {
					particleList.splice(i, 1);
				}
			}
			
			//雪のキラキラをするために1回小さくする
			bdSmall.draw(bdSnow, mtSmall);
			bdSmall.applyFilter(bdSmall, bdSmall.rect, pt, new BlurFilter(2, 2, 1));
			bdSnow.draw(bdSmall, mtBig, cl, BlendMode.SCREEN);
			
			//軽くブラーをかけてみる
			bdSnow.applyFilter(bdSnow, bdSnow.rect, pt, new BlurFilter(2, 2, 1));
		}
		
		private function addParticle():void {
			//雪オブジェクトの追加
			var tw:Particle = new Particle();
			particleList.push(tw);
			tw.x = Math.random() * 640;
			tw.y = 1;
		}
	}
}
import flash.display.Bitmap;
class Particle {
	public var x:Number;
	public var y:Number;
	public var del:Boolean = false;
	private var vy:Number;
	private var ay:Number;
	function Particle() {
		vy = 0;
		ay = 0.03;
	}
	public function update(b:Boolean):void {
		if (b) {
			//境界線上の速度
			vy = 0.01;
		} else {
			//何もないとこの速度
			vy += ay;
			if (vy > 3) {
				vy = 3;
			}
		}
		y += vy;
		if (y > 480) {
			//画面から消えたらdel=trueにすることで削除対象になる
			del = true;
		}
	}
}


| | コメント (0) | トラックバック (0)

2008年10月27日 (月)

FLASHで「L.A.S.E.R Tag」みたいなやつのテスト

L.A.S.E.R Tag」みたいなのをFLASHで作りたいなーと思ってちょっとやってみました。

簡単に言うとカメラの映像の中から指定した色の部分を検出し、それと同じ座標に描画していけばいいのかなと。
プロジェクターで出力した部分をカメラで取得することで、実際にそこに描いてるかのように見せられる。

いくつか必要なものがあるけど、ちゃんとしたものが無いので以下のような感じでやりました。

<プロジェクタ>
あるけど出すの面倒なので、モニターで。。

<レーザーポインタ>
無いのでペンの先に赤いのをつけてそれで代用。

<カメラ>
安いWEBカメラで。もっといいやつなら動きもよくなるかと。
これだとフレームレート5~7とかなので。

ということでやってみたのがこれ。(要:FlashPlayer10)

やってみた映像

左上がカメラの映像。モニターを映してみてください。出力した映像(下の大きいエリア=描画エリア)が収まるようにします。
左上の映像の四隅の青い四角を移動させて描画エリアを指定。(映像を見てもらえれば分かるかな・・・)
真ん中の映像が描画エリアだけになります。
右上が描画エリア内の赤い部分の検出状況。
今回モニターでやってますが、モニターの描画エリア部分のとこで赤いものを動かすとそれが検出されて、その部分に描画されていきます。

処理の流れは以下のとおりです。

1.カメラの映像を取得
2.キャプチャーするエリア(描画エリア)だけ抜き出す
3.赤い部分を検出する
4.検出した部分の座標を使って描画

●1.カメラの映像を取得
これは特に説明する必要もない感じです。

●2.キャプチャーするエリア(描画するエリア)だけ抜き出す
プロジェクタで映し出した部分(今回はモニター)をカメラで撮るわけですが、余計な部分も入ってしまいます。
それに正面にカメラをおけるわけではないので、パースがついてしまいます。
これを必要な部分だけをパースがつかない状態にするために、カメラ固定後に描画エリアの四隅を指定するようにします。
今回のでは左上の映像の四隅に青い四角が置いてありますが、それを左上の映像内の描画エリアの四隅に移動させます。
そうすると上の真ん中の映像が描画エリアだけになります。

これをするためにFlashPlayer10からのdrawTrianglesを使いました。
(参考)
http://www.senocular.com/flash/tutorials/flash10drawingapi/

●3.赤い部分を検出する
こちらを参考にしました。
http://www.hatayan.org/weblog/archives/2005/11/30/123132.php

BitmapDataの赤のチャンネルから、青のチャンネル+緑のチャンネルを引いたものが赤い部分になります。

●4.検出した部分の座標を使って描画
これは単にマウスで描くのと同じで、マウス座標使うか検出したエリアの座標使うかの違いだけです。
描画方法はいろいろありますけど、とりあえず簡単なやつで。

精度とか置いといて一応動いた。実際プロジェクタとレーザーポインタでもっと遠い距離でやってみてどうかかな。
こういう仕組みを使ってなんかおもしろいことできればいいなーと思ってます。

package {
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.events.StatusEvent;
	import flash.filters.BlurFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.ColorTransform;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.display.BlendMode;
	public class LaserTag extends Sprite {
		private var cam:Camera;
		private var video:Video;
		private var bdCaptureArea:BitmapData;
		private var bdDetector:BitmapData;
		private var bdOutput:BitmapData;
		private var mt:Matrix;
		private var pt:Point;
		private var ct:ColorTransform
		private var box:Shape;
		
		private var cameraPoint1:Sprite;
		private var cameraPoint2:Sprite;
		private var cameraPoint3:Sprite;
		private var cameraPoint4:Sprite;
		
		private var tri1:Shape;
		private var mouseCursor:Shape;
		private var endX:Number = 0;
		private var endY:Number = 0;
		public function LaserTag() {
			addEventListener(Event.ADDED_TO_STAGE, added);
		}
		private function added(e:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, added);
			init();
		}
		private function init():void {
			//よく使うやつを作っとく。
			mt = new Matrix();
			pt = new Point();
			ct = new ColorTransform();
			
			//カメラ
			cam = Camera.getCamera();
			cam.setMode(320, 240, 30, false);
			video = new Video();
			video.attachCamera(cam);
			addChild(video);
			
			//カメラの動画内のキャプチャーする部分用
			bdCaptureArea = new BitmapData(320, 240, false, 0x000000);
			//↑の赤い部分だけを残したやつ
			bdDetector = new BitmapData(320, 240, false, 0x000000);
			//出力用
			bdOutput = new BitmapData(960, 720, false, 0x000000);
			
			//それぞれaddChild
			var bm1:Bitmap = new Bitmap(bdCaptureArea);
			addChild(bm1);
			bm1.x = 320;
			var bm2:Bitmap = new Bitmap(bdDetector);
			addChild(bm2);
			bm2.x = 640;
			var bm3:Bitmap = new Bitmap(bdOutput);
			addChild(bm3);
			bm3.x = 0;
			bm3.y = 240;
			
			//赤い部分の検出エリアを分かりやすくするためのBOX
			box = new Shape();
            box.graphics.lineStyle(1, 0x0000FF);
            box.graphics.drawRect(0, 0, 100, 100);
            addChild(box);
			
			//キャプチャー部分を長方形にする用
			tri1 = new Shape();
			tri1.x = 320;
            addChild(tri1);
			
			//描画用の青い丸
			mouseCursor = new Shape();
			mouseCursor.graphics.beginFill(0x000099, 0.8);
			mouseCursor.graphics.drawCircle( -10, -10, 20);
			mouseCursor.graphics.endFill();
			
			//キャプチャー範囲指定用の4つのポイントを作成
			setCameraPoints();
			
			//ENTER_FRAMEスタート
			addEventListener(Event.ENTER_FRAME, loop);
			
			//画面クリックで出力用BitmapDataを初期化
			stage.addEventListener(MouseEvent.MOUSE_DOWN, reflesh);
		}
		private function reflesh(e:MouseEvent):void {
			//黒く塗りつぶす
			bdOutput.draw(new BitmapData(960, 720, false, 0x000000));
		}
		
		private function loop(e:Event):void {
			//カメラの映像をBitmapDataにdraw
			var bdVideo:BitmapData = new BitmapData(320, 240);
			bdVideo.draw(video);
			
			//4つのcameraPointの範囲の部分を長方形にする
			tri1.graphics.clear();
			tri1.graphics.beginBitmapFill(bdVideo);
            tri1.graphics.lineStyle(1, 0x0000FF);
			tri1.graphics.drawTriangles(
				Vector.([0, 0,			//左上
								320, 0,			//右上
								0, 240,			//左下
								320, 240]),		//右下
				Vector.([0,1,2, 1,3,2]),
				Vector.([cameraPoint1.x/320, cameraPoint1.y/240,	//左上
								cameraPoint2.x/320, cameraPoint2.y/240,		//右上
								cameraPoint3.x/320, cameraPoint3.y/240,		//左下
								cameraPoint4.x/320, cameraPoint4.y/240]));	//右下
			tri1.graphics.endFill();
			
			//長方形にしたものをdraw
			bdCaptureArea.draw(tri1);
			
			//赤い部分を検知
			var bdBlue:BitmapData = new BitmapData(320, 240);
			var bdGreen:BitmapData = new BitmapData(320, 240);
			var bdRed:BitmapData = new BitmapData(320, 240);
			bdBlue.copyChannel(bdCaptureArea, bdCaptureArea.rect, pt, BitmapDataChannel.BLUE, BitmapDataChannel.RED);
			bdGreen.copyChannel(bdCaptureArea, bdCaptureArea.rect, pt, BitmapDataChannel.GREEN, BitmapDataChannel.RED);
			bdRed.copyChannel(bdCaptureArea, bdCaptureArea.rect, pt, BitmapDataChannel.RED, BitmapDataChannel.RED);
			bdBlue.draw(bdGreen, mt, ct, BlendMode.LIGHTEN);
			bdRed.draw(bdBlue, mt, ct, BlendMode.SUBTRACT);
			bdDetector.draw(bdRed);
			bdDetector.threshold(bdDetector, bdDetector.rect, pt, ">", 0xff220000, 0xffFF0000);
			
			//赤い部分の短形を取得
			var rect:Rectangle = bdDetector.getColorBoundsRect(0xffFF0000, 0xffFF0000, true);
			
			//赤い部分を囲むようにBOXを移動、変形
			box.width = rect.width;
			box.height = rect.height;
			box.x = rect.x + 640;
			box.y = rect.y;
			
			//検出されてる場合はその座標を設定
			if (rect.x > 0 && rect.y > 0) {
				endX = rect.x * 3;
				endY = rect.y * 3;
			}
			
			//検出された部分に青い丸が向かってく
			mouseCursor.x += (endX - mouseCursor.x) / 10;
			mouseCursor.y += (endY - mouseCursor.y) / 10;
			
			//青い丸を描画&毎回全体にブラーをかける
			bdOutput.applyFilter(bdOutput, bdOutput.rect, pt, new BlurFilter(2, 2, 3));
			var mtMouse:Matrix = new Matrix(1, 0, 0, 1, mouseCursor.x, mouseCursor.y);
			bdOutput.draw(mouseCursor, mtMouse);
		}
		
		private function setCameraPoints():void {
			//キャプチャー範囲指定用のCameraPointを設置
			cameraPoint1 = new CameraPoint();
			cameraPoint2 = new CameraPoint();
			cameraPoint3 = new CameraPoint();
			cameraPoint4 = new CameraPoint();
			
			addChild(cameraPoint1);
			addChild(cameraPoint2);
			addChild(cameraPoint3);
			addChild(cameraPoint4);
			
			cameraPoint2.x = 320;
			cameraPoint3.y = 240;
			cameraPoint4.x = 320;
			cameraPoint4.y = 240;
		}
	}
}
import flash.display.Sprite;
import flash.display.Shape;
import flash.events.Event;
import flash.events.MouseEvent;
class CameraPoint extends Sprite {
	
	public function CameraPoint() {
		addEventListener(Event.ADDED_TO_STAGE, added);
	}
	
	private function added(e:Event):void {
		removeEventListener(Event.ADDED_TO_STAGE, added);
		init();
	}
	
	private function init():void {
		var box:Shape = new Shape();
		box.graphics.beginFill(0x0000FF);
		box.graphics.lineStyle(1, 0x0000FF);
		box.graphics.drawRect(-10, -10, 20, 20);
		box.graphics.endFill();
		addChild(box);
		
		buttonMode = true;
		addEventListener(MouseEvent.MOUSE_DOWN, startMove);
	}
	
	private function startMove(e:MouseEvent):void {
		addEventListener(Event.ENTER_FRAME, loop);
		stage.addEventListener(MouseEvent.MOUSE_UP, stopMove);
	}
	private function stopMove(e:MouseEvent):void {
		removeEventListener(Event.ENTER_FRAME, loop);
		stage.removeEventListener(MouseEvent.MOUSE_UP, stopMove);
	}
	
	private function loop(e:Event):void {
		x = stage.mouseX;
		y = stage.mouseY;
	}
}

| | コメント (0) | トラックバック (0)

2008年10月17日 (金)

アルファをかけた時の色の違い

背景色と同じ色のオブジェクトを置くと、もちろん色が同じなのでどこに置いたか分からなくなります。
ではそのオブジェクトにアルファをかけるとどうなるか。
背景色と同じ色だからアルファをかけても見た目には変わらないように思えます。
でも実際は色味が微妙に変わってしまい、オブジェクトの境界線が見えてしまう場合があります。

世間的には当たり前のことかもしれないけど、個人的にはさっき気付いた。

どういう時に変わってしまうのか軽く検証してみました。

・明るい色の場合は大丈夫
・暗い色の時、変わってしまう場合がある
・RGBの各値が偶数だと大丈夫で奇数だとだめっぽい
・RGBの各値が80以上のところは大丈夫っぽい

全部調べたわけでもなく、原因も分からないので正確じゃないかもしれないけど
RGBの各値で、80以下で奇数の部分が無いようにすればいいっぽい。

↓テストしてみたやつ。

左側の背景色が「#130900」、右側が「#140800」となってます。
左右の数値の上に背景色と同じ色の四角いオブジェクトが置いてあります。アルファを50%にしてます。
左側はよく見ると四角の境界線が見えます。リンク先のswfのほうが分かりやすいかも。
左側は「#130900」でRとGが奇数です。
右側はRとGを偶数にして「#140800」としたものです。こうすると境界線が見えなくなります。
少なくとも僕の環境では。
環境によってはこれでも見えちゃうのかな。。

<追記:2008/10/20>
上記の話はアルファが50%の時だけっぽい。。
アルファ50以下、50以上で値が変わってくる。うーん。。。

| | コメント (0) | トラックバック (0)

2008年10月16日 (木)

Google Maps API for FLASHがいつの間にかバージョン変わってた。。

Google Maps API for FLASHが何も変えてないはずなのにエラーが出るようになったのでサイトを見てみたらいつの間にかバージョンが1.7に・・・さっきまで1.3使ってた。たまにチェックしてたんだけどなあ。
しかも前まではflexしか対応してなかったと思うけど、FLASH CS3でも使用可能に。
Flexで作ってたけどFLASHに移行しよう。慣れてるからかFLASHのほうがやっぱりやりやすい。

Flexのほうでswcを1.3から1.7に変更してそのままパブリッシュしたら再生中にエラーが出たけど、同じソースでFLASH CS3で1.7のを使ってやったら特に問題無し。
原因は探らない。。

| | コメント (0) | トラックバック (0)

2008年10月10日 (金)

FlashPlayer10 読み込んだ音にエフェクトをかける

さっきのやつで、読み込んだMP3に対してエフェクトをかけるのは下記のとおり。
「music.mp3」という名前のMP3を用意してswfと同じとこにおいてください。

ブログのデザイン変えようかな。。横幅広げないと見にくい。

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.media.Sound;
	import flash.utils.ByteArray;
	import flash.net.URLRequest;
	import flash.events.SampleDataEvent;
	import org.sazameki.audio.engine.ssGenerator.SsAudioSetting;
	import org.sazameki.audio.processor.effects.Distortion;
	import org.sazameki.audio.core.Sample;
	public class Main6 extends Sprite {
		private const BUFFER_LENGTH:Number = 8192;
		private var dynamicSound:Sound;
		private var dist:Distortion;
		private var distNo:int = 0;
		private var ssaSetting:SsAudioSetting;
		private var mp3sound:Sound;
		private var samples:ByteArray;
		
		public function Main6():void {
			if (stage) {
				init();
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			//チャンネル数やサンプルレートなどの設定。通常は引数無しでOK?
			ssaSetting = new SsAudioSetting();
			//Distortionインスタンスの作成
			dist = new Distortion();
			//Distortionの設定
			//第一引数はさっきの設定(SsAudioSetting)、第二引数にgain値を渡す(配列で)
			//(Distortionはパラメータ1つだけど、他のReverbとかでパラメータが複数ある関係で配列になってるかと)
			dist.initialize(ssaSetting, [0.0]);
			
			//Soundオブジェクトの作成
			dynamicSound = new Sound();
			
			//MP3の読み込み
			mp3sound = new Sound();
			mp3sound.addEventListener(Event.COMPLETE, loadComplete);
			mp3sound.load(new URLRequest("music.mp3"));
			
			//MP3の波形データを入れる用のByteArray
			samples = new ByteArray();
			
			//Distortionのgain値の切り替え用マウスイベント
			stage.addEventListener(MouseEvent.MOUSE_DOWN, changeDist);
		}
		
		private function loadComplete(event:Event = null):void {
			//MP3が読み込み終わったら
			
			//sampleDataイベントを設定
			dynamicSound.addEventListener("sampleData", sampleData);
			//再生
			dynamicSound.play();

		}
		
		private function changeDist(e:MouseEvent):void {
			//マウスクリックの度にgain値が0.0→0.3→0.6→0.0・・・と変わっていきます
			distNo++;
			if (distNo > 2) {
				distNo = 0;
			}
			//gain値を変えるためにinitializeで再設定。こんなやり方でいいのかな??
			//Distortion.asにgainのsetterを入れてしまうという手も。。
			if (distNo == 1) {
				dist.initialize(ssaSetting, [0.3]);
			} else if (distNo == 2) {
				dist.initialize(ssaSetting, [0.6]);
			} else {
				dist.initialize(ssaSetting, [0.0]);
			}
		}
		
		private function sampleData(event:SampleDataEvent):void {
			samples.position = 0;
			//MP3から波形を取得
			var len:Number = mp3sound.extract(samples,BUFFER_LENGTH);
			if (len < BUFFER_LENGTH) {
				len += mp3sound.extract(samples, BUFFER_LENGTH - len, 0);
			}
			samples.position = 0;
			
			var sss:Array = [];
			var c:int;
			for (c = 0; c < len; c++) {
				//MP3の波形を取得
				var left:Number = samples.readFloat();
				var right:Number = samples.readFloat();
				//Sampleインスタンスの作成
				var sam:Sample = new Sample();
				//左右のチャンネルに値を設定
				sam.left = left;
				sam.right = right;
				//配列にSampleを入れていく
				sss.push(sam);
			}
			
			//Sampleの入った配列を渡してDistortionをかける
			dist.processAudio(sss);
			
			//波形を書き込む
			for (c = 0; c < BUFFER_LENGTH; c++) {
				event.data.writeFloat(sss[c].left / 5);
				event.data.writeFloat(sss[c].right / 5);
			}
		}
	}
}



| | コメント (0) | トラックバック (0)

FlashPlayer10 音にエフェクトをかける

音でいろいろ遊びたい・・・と、いつも思ってるんだけど難しい。。
player10で波形をいじれるようになったということで時々試してるけどなかなか進まず。

動的な音の生成方法というのはなんとなく分かったけども、それをどうやったら音を変化させることができるのか、というのが全然分からん。

そんな時に↓こちらを見つけて、便利なライブラリもあったのでちょっと使ってみました。
http://www.adobe.com/jp/devnet/flash/articles/flp10_sound.html

とりあえずやってみたいのは音にエフェクトをかけること。delayとかreverbとか。

上記の中で見つけたライブラリ「sazameki」の中を見てみると「org.sazameki.audio.processor.effects」以下にそれっぽいクラスが!
このライブラリの基本的な使い方も分からんのだけど、エフェクトのクラスを使いたいがために逆にたどっていってなんとか使うことができました。できた気がするだけかもしれないけども。。

一応、下記のように書いたらDistortionをかけることができました。
画面には何も表示されません。音が鳴るはずです。マウスクリックでDistortionのgain値が変わります。

ちなみにDistortion.as内の「processAudio()」の関数にDistortionをかけるための計算が書いてあるので、そこだけ持ってきてもできそうな感じがしないでもないです。

同様にReverbもやってみましたが、処理がすごく重かったです。パラメータがおかしかったのかも!?
DelayもReverb、Distortionとは少しやり方違ってましたが同じ感じでできました。

まずは波形の意味をちゃんと理解したい。

※Flash Player10対応です。

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.media.Sound;
	import flash.utils.ByteArray;
	import flash.events.SampleDataEvent;
	import org.sazameki.audio.engine.ssGenerator.SsAudioSetting;
	import org.sazameki.audio.processor.effects.Distortion;
	import org.sazameki.audio.core.Sample;
	public class Main5 extends Sprite {
		private const BUFFER_LENGTH:Number = 8192;
		private var dynamicSound:Sound;
		private var dist:Distortion;
		private var distNo:int = 0;
		private var ssaSetting:SsAudioSetting;
		
		public function Main5():void {
			if (stage) {
				init();
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			//チャンネル数やサンプルレートなどの設定。通常は引数無しでOK?
			ssaSetting = new SsAudioSetting();
			//Distortionインスタンスの作成
			dist = new Distortion();
			//Distortionの設定
			//第一引数はさっきの設定(SsAudioSetting)、第二引数にgain値を渡す(配列で)
			//(Distortionはパラメータ1つだけど、他のReverbとかでパラメータが複数ある関係で配列になってるかと)
			dist.initialize(ssaSetting, [0.0]);
			
			//Soundオブジェクトの作成
			dynamicSound = new Sound();
			//sampleDataイベントを設定
			dynamicSound.addEventListener("sampleData", sampleData);
			//再生
			dynamicSound.play();
			
			//Distortionのgain値の切り替え用マウスイベント
			stage.addEventListener(MouseEvent.MOUSE_DOWN, changeDist);
		}
		private function changeDist(e:MouseEvent):void {
			//マウスクリックの度にgain値が0.0→0.3→0.6→0.0・・・と変わっていきます
			distNo++;
			if (distNo > 2) {
				distNo = 0;
			}
			//gain値を変えるためにinitializeで再設定。こんなやり方でいいのかな??
			//Distortion.asにgainのsetterを入れてしまうという手も。。
			if (distNo == 1) {
				dist.initialize(ssaSetting, [0.3]);
			} else if (distNo == 2) {
				dist.initialize(ssaSetting, [0.6]);
			} else {
				dist.initialize(ssaSetting, [0.0]);
			}
		}
		private function sampleData(event:SampleDataEvent):void {
			var sss:Array = [];
			var c:int;
			for (c = 0; c < BUFFER_LENGTH; c++) {
				//ピーっていう音を生成
				//この代わりにMP3とかの音から波形を取得するのも可能
				var rad:Number = Number(c + event.position)/Math.PI/2;
				var amp:Number = Math.sin(rad) / 4;
				//Sampleインスタンスの作成
				var sam:Sample = new Sample();
				//左右のチャンネルに値を設定
				sam.left = amp;
				sam.right = amp;
				//配列にSampleを入れていく
				sss.push(sam);
			}
			
			//Sampleの入った配列を渡してDistortionをかける
			dist.processAudio(sss);
			
			//波形を書き込む
			for (c = 0; c < BUFFER_LENGTH; c++) {
				event.data.writeFloat(sss[c].left / 5);	//音がでかいので5で割って小さくしてます
				event.data.writeFloat(sss[c].right / 5);
			}
		}
	}
}


| | コメント (2) | トラックバック (0)

2008年8月20日 (水)

ぼかしを最初に作っておく。

http://clockmaker.jp/blog/2008/08/papervision3d_snowfall_particle/

こちらの「初期ロードの際に「ぼけ」をレンダリング」というのを見てテストしてみた。

1)毎回ブラーをかける
ただの白丸にEnterFrameで毎回ブラーをかけます。
ブラーの段階は50段階になるようにしてます。
白丸の数は20個だけど、フレームレート30で設定してるのに半分くらいに。。
http://www.primevision.cc/test/flash/speed/test01.html

2)最初にレンダリングしておく
最初に50段階分のぼかしを作り、BitmapDataにdrawしておきます。
動きは1と同じで、ぼかしをかける代わりに、その段階に対応するBitmapDataをattachBitmapしてます。
こっちは200個だけど、フレームレート30くらい出てます。
http://www.primevision.cc/test/flash/speed/test01b.html

一応ソース

こんなに差があるんですね。。
コサインとかの計算結果をあらかじめ計算して配列に入れておく、とかいう処理を速くするための方法も前から知ってるけど、こういうのはやっぱちゃんとやったほうがいいですね。
今の世の中当たり前なのかな??工夫していかねば。。

| | コメント (0) | トラックバック (0)

2008年5月27日 (火)

FlashPlayer10 ローカルの画像を読み込み→カスタムフィルタ→別名保存

今度はローカルの画像を読み込んでみます。
読み込むまではテキストファイルの時と一緒。
違うのは読み込んだ後、Loader.loadBytes()でByteArrayから読み込まないといけないってことです。
そのLoaderインスタンスをaddChildしてあげればOKです。

これだけだとあれなので、PixelBenderで作成したカスタムフィルタをかけてみます。
前に作ったセピア色に変換するやつです。

カスタムフィルタをかけるのは前と変わらず。
まず読み込んだ画像と同じ大きさのBitmapDataを作成しておきます。
それにdrawしてapplyFilterでフィルタをかけます。

ここで注意。
こちらにも書かれてますが、loadBytesで読み込んだ直後はwidth、heightを取得できません。
Loader.contentLoaderInfoでEvent.INITが呼ばれた後は取得できるようです。

次に保存ですが、このままだと保存できません。
JPGEncoder」を使ってBitmapDataをByteArrayに変換してあげます。
それをFileReferenceのsave()に渡してあげればOKです。
やっぱり存在しているファイルに上書きするとIOErrorになるけど。。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.net.FileReference;
	import flash.events.*;
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.geom.Point;
	import flash.utils.ByteArray;
	import com.adobe.images.JPGEncoder;
	
	public class Main extends Sprite {
		[Embed(source = "test03.pbj", mimeType = "application/octet-stream")]
		private var shaderData:Class;
		private var ld:Loader;
		public function Main() {
			//読み込みボタン
			var bd1:BitmapData = new BitmapData(120, 60, false, 0xFF6600);
			var bm1:Bitmap = new Bitmap(bd1);
			var mc1:Sprite = new Sprite();
			mc1.addChild(bm1);
			mc1.x = 20;
			mc1.y = 20;
			addChild(mc1);
			mc1.buttonMode = true;
			mc1.addEventListener(MouseEvent.CLICK, selectFile);			
		}
		private function selectFile(e:MouseEvent):void {
			//読み込むファイルの選択
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.SELECT, loadFile);
			ff.browse();
		}
		private function loadFile(e:Event):void {
			//ファイル読み込み
			var ff:FileReference = FileReference(e.target);
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, setImage );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.load();
		}
		private function setImage(e:Event):void {
			//ByteArrayに読み込まれたデータをLoaderで読み込み
			ld = new Loader();
			ld.loadBytes(e.target.data);
			addChild(ld);	//読み込んだ画像を表示
			ld.contentLoaderInfo.addEventListener(Event.INIT,initImage);
		}
		private function initImage(e:Event):void {
			//読み込んだ画像にカスタムフィルタ(セピア)をかける
			var shader:Shader = new Shader(ByteArray(new shaderData()));
			var shaderFilter:ShaderFilter = new ShaderFilter(shader);

			var bd:BitmapData = new BitmapData(ld.width, ld.height);
			var bm:Bitmap = new Bitmap(bd);
			bm.x = ld.width;
			bd.draw(ld);
			bd.applyFilter(bd, bd.rect, new Point(), shaderFilter);
			addChild(bm);	//カスタムフィルタをかけた画像を表示
			
			//JPGEncoderでBitmapDataをByteArrayに変換
			var jpgEncoder:JPGEncoder = new JPGEncoder(85);
			var jpgStream:ByteArray = jpgEncoder.encode(bd);
			saveImage(jpgStream);
		}
		private function saveImage(ba:ByteArray):void {
			//保存
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, function(e:Event):void { trace("complete");  } );
			ff.addEventListener(Event.CANCEL, function(e:Event):void { trace("cancel");  } );
			ff.addEventListener(Event.SELECT, function(e:Event):void { trace("select");  } );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.save(ba,"newImage.jpg");
		}
	}
	
}

| | コメント (0) | トラックバック (2)

FlashPlayer10 テキストファイルを開く、保存する

前回のでテイストファイルをローカルに直接保存しましたが、今度は
ローカルのテキストファイルを開き、編集して保存してみます。

やってみたんだけど、「上書き保存」ができず。。
存在しているファイルと同じ名前で保存しようとするとIOErrorになります。
これはなんででしょう・・・仕様?

別名保存であれば下記のスクリプトでいけます。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.net.FileReference;
	import flash.events.*;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	public class Main extends Sprite{
		private var txt:TextField;
		public function Main() {
			//テキスト入力
			txt = new TextField();
			txt.width = 400;
			txt.multiline = true;
			txt.text = "";
			txt.border = true;
			txt.type = TextFieldType.INPUT;
			txt.x = 20;
			txt.y = 20;
			addChild(txt);
			
			//読み込みボタン
			var bd1:BitmapData = new BitmapData(120, 60, false, 0xFF6600);
			var bm1:Bitmap = new Bitmap(bd1);
			var mc1:Sprite = new Sprite();
			mc1.addChild(bm1);
			mc1.x = 20;
			mc1.y = 200;
			addChild(mc1);
			mc1.buttonMode = true;
			mc1.addEventListener(MouseEvent.CLICK, selectFile);			

			//保存ボタン
			var bd2:BitmapData = new BitmapData(120, 60, false, 0x000000);
			var bm2:Bitmap = new Bitmap(bd2);
			var mc2:Sprite = new Sprite();
			mc2.addChild(bm2);
			mc2.x = 160;
			mc2.y = 200;
			addChild(mc2);
			mc2.buttonMode = true;
			mc2.addEventListener(MouseEvent.CLICK, saveFile);			
		}
		private function selectFile(e:MouseEvent):void {
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.SELECT, loadFile);
			ff.browse();

		}
		private function loadFile(e:Event):void {
			var ff:FileReference = FileReference(e.target);
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, setText );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.load();
		}
		private function setText(e:Event):void {
			txt.text = e.target.data;
		}
		private function saveFile(e:MouseEvent):void {
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, function(e:Event):void { trace("complete");  } );
			ff.addEventListener(Event.CANCEL, function(e:Event):void { trace("cancel");  } );
			ff.addEventListener(Event.SELECT, function(e:Event):void { trace("select");  } );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError",e.target,e.text);  } );
			ff.save(txt.text,"saveTextTest.txt");

		}
	}
	
}

| | コメント (1) | トラックバック (0)