« 2007年08月 | メイン | 2007年10月 »

2007年09月28日

BitmapDataの範囲拡張&収縮

てっく煮ブログ:BitmapDataの範囲拡張をするなら・・・では全ピクセルをチェックしていますが、それよりも圧倒的に軽く処理できる方法があるので紹介しておきます。






マウスで適当に線を描いて、上下キーで拡張・収縮できます。
別段難しいことをしているわけではなく、コンボリューションフィルタ使った結果をdrawしてるだけなのですが、ちょっと手抜きしたので拡張があまり綺麗じゃない・・・
もう少し作り込めば収縮と同じような感じで拡張を行えるはずですが、いずれにせよこの手の処理はビットマップのメソッドだけでやれることが多いのでアルゴリズムを考えてみると楽しいと思います。

(ブラーフィルタかけてからスレッショルドで削ることでマシになりました。ソースも微修正)


ソースは以下。

package {
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Graphics;
  import flash.display.Sprite;
  import flash.display.Shape;
  import flash.events.Event;
  import flash.events.KeyboardEvent;
  import flash.events.MouseEvent;
  import flash.filters.BlurFilter;
  import flash.filters.ConvolutionFilter;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.ui.Keyboard;
  import flash.geom.ColorTransform;
  
  [SWF(backgroundColor=0xffffff, frameRate='60', width='320', height='240')]
  
  public class DilateAndErode extends Sprite {
    private var bd:BitmapData;
    private var temp:BitmapData;
    private var line:Sprite;
    
    private var rect:Rectangle;
    private var zero:Point;
    private var mtx:Matrix;
    
    private var dilate_bf:BlurFilter;
    private var erode_cf:ConvolutionFilter;
    
    
    public function DilateAndErode() {
      bd = new BitmapData( stage.stageWidth, stage.stageHeight, false, 0xffffff );
      temp = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0x00ffffff );
      
      addChild( new Bitmap( bd ) );
      line = addChild( new Sprite() ) as Sprite;
      
      var frame:Shape = addChild( new Shape() ) as Shape;
      frame.graphics.lineStyle( 2, 0 );
      frame.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
      
      rect = bd.rect;
      zero = rect.topLeft;
      mtx = new Matrix();
      
      dilate_bf = new BlurFilter( 4, 4, 3 );
      erode_cf = new ConvolutionFilter( 3, 3, [ 0, 1, 0, 1, 1, 1, 0, 1, 0 ], 5, 0, true );
      
      stage.addEventListener( MouseEvent.MOUSE_DOWN, mouseDownHandler );
      stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
    }
    
    private function mouseDownHandler( e:MouseEvent ):void {
      var g:Graphics = line.graphics;
      g.lineStyle( 5, 0x000000, 1 );
      g.moveTo( stage.mouseX, stage.mouseY );
      
      var mouseMoveFunc:Function = function( e:MouseEvent ):void {
        g.lineTo( stage.mouseX, stage.mouseY );
      }
      var mouseUpFunc:Function = function( e:MouseEvent ):void {
        stage.removeEventListener( MouseEvent.MOUSE_MOVE, mouseUpFunc );
        stage.removeEventListener( MouseEvent.MOUSE_UP, mouseUpFunc );
        
        bd.draw( line );
        g.clear();
      }
      
      stage.addEventListener( MouseEvent.MOUSE_MOVE, mouseMoveFunc );
      stage.addEventListener( MouseEvent.MOUSE_UP, mouseUpFunc );
    }
    
    
    private function keyDownHandler( e:KeyboardEvent ):void {
      switch ( e.keyCode ) {
        case Keyboard.UP:
          temp.applyFilter( bd, rect, zero, dilate_bf );
          temp.threshold( temp, rect, zero, ">", 0x000000c0, 0xffffffff, 0x000000ff );
          bd.draw( temp, mtx, null, "multiply");
          break;
        case Keyboard.DOWN:
          temp.applyFilter( bd, rect, zero, erode_cf );
          bd.draw( temp, mtx, null, "add");
          break;
      }
    }
  }
}

2007年09月26日

as3版かまいたちの夜カメラ

特に習作というわけでもないのだけれど、前に作ったかまいたちの夜カメラをas3で書いてみた。






クリックすると映像を固定。
以後、差分がいわゆるかまいたち。
上下キーでスレッショルド調整ができるけど、おまけ程度。

ほんとこれが ustream 配信できれば面白いのにな。
仮想カメラデバイスなソフトを作るスキルなんてないので残念。


あまり美しくないけど、ソースは以下。
よくよく考えたら今までソース丸ごと公開したことなかった気がする。

package {
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageQuality;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.KeyboardEvent;
  import flash.events.MouseEvent;
  import flash.events.TimerEvent;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.media.Camera;
  import flash.media.Video;
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import flash.ui.Keyboard;
  import flash.utils.Timer;
  
  
  [SWF(backgroundColor=0x000000, frameRate='30', width='320', height='240')]
  
  public class KamaCamera extends Sprite {
    private const CAMERA_W:int = 320;
    private const CAMERA_H:int = 240;
    private const COLOR_SILHOUETTE:uint = 0x603300cc;
    private const DEFAULT_THRESHOLD:uint = 0x20;
    
    private var camera:Camera;
    private var video:Video;
    
    private var rect:Rectangle;
    private var zero:Point;
    private var mtx:Matrix;
    
    private var threshold:uint;
    private var threshold_txt:TextField;
    private var timer:Timer;
    
    private var temp_bd:BitmapData;
    private var fixed_bd:BitmapData;
    private var kama_bd:BitmapData;
    
    
    public function KamaCamera() {
      stage.align = StageAlign.TOP_LEFT;
      stage.quality = StageQuality.BEST;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      
      timer = new Timer( 1000, 1 );
      timer.addEventListener( TimerEvent.TIMER_COMPLETE, timerHandler );
      
      camera = Camera.getCamera();
      
      if ( camera != null ) {
                setup();
         }
    }
    
    private function setup():void {
      var format:TextFormat = new TextFormat( null, 120, 0xffffff );
      format.align = "center";
      
      threshold_txt = new TextField();
      threshold_txt.mouseEnabled = false;
      threshold_txt.autoSize = TextFieldAutoSize.CENTER;
      threshold_txt.defaultTextFormat = format;
      threshold_txt.text = String( threshold );
      threshold_txt.x = ( CAMERA_W - threshold_txt.width ) / 2;
      threshold_txt.y = ( CAMERA_H - threshold_txt.height ) / 2;
      threshold_txt.visible = false;
      setThreshold( DEFAULT_THRESHOLD );
      
      video = new Video( CAMERA_W, CAMERA_H );
      video.attachCamera( camera );
      addChild( video );
      
      rect = new Rectangle( 0, 0, CAMERA_W, CAMERA_H );
      zero = new Point();
      mtx = new Matrix();

temp_bd = new BitmapData( CAMERA_W, CAMERA_H, true, 0x00000000 );
fixed_bd = temp_bd.clone();
kama_bd = temp_bd.clone();

var func:Function = function( e:MouseEvent ):void {
fix();
update();
removeChild( video );
addChild( new Bitmap( kama_bd ) );
addChild( threshold_txt );

stage.removeEventListener( e.type, arguments.callee );
stage.addEventListener( MouseEvent.CLICK, fix );
stage.addEventListener( Event.ENTER_FRAME, update );
stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
}

stage.addEventListener( MouseEvent.CLICK, func );
}

private function fix( e:MouseEvent = null ):void {
fixed_bd.draw( video );
}

private function update( e:Event = null ):void {
temp_bd.copyPixels( fixed_bd, rect, zero );
temp_bd.draw( video, mtx, null, "subtract");
temp_bd.threshold( temp_bd, rect, zero, ">", threshold << 16, COLOR_SILHOUETTE, 0x00FF0000, false );
temp_bd.threshold( temp_bd, rect, zero, ">", threshold << 8, COLOR_SILHOUETTE, 0x0000FF00, false );
temp_bd.threshold( temp_bd, rect, zero, ">", threshold, COLOR_SILHOUETTE, 0x000000FF, false );
temp_bd.threshold( temp_bd, rect, zero, "==", 0xff000000, 0x00000000, 0xFF000000, false );

kama_bd.lock();
kama_bd.copyPixels( fixed_bd, rect, zero );
kama_bd.draw( temp_bd );
kama_bd.unlock();
}

private function setThreshold( value:int ):void {
threshold = Math.min( Math.max( value, 0x00 ), 0xff );
threshold_txt.text = String( threshold );
threshold_txt.visible = true;

timer.reset();
timer.start();
}

private function keyDownHandler( e:KeyboardEvent ):void {
switch ( e.keyCode ) {
case Keyboard.UP:
setThreshold( threshold + 1 );
break;
case Keyboard.DOWN:
setThreshold( threshold - 1 );
break;
}
}

private function timerHandler( e:TimerEvent ):void {
threshold_txt.visible = false;
}
}
}

2007年09月24日

twitterのアイコンとレッテル

twitterではアイコン選定に相当気を使わないと、あらぬレッテルを貼られかねないよと言ってみる。


各発言には必ずアイコンがついてまわるのだけれども、大勢の発言がずらーっと並んでいる中をアイコンで識別することが多いからなのか、長く利用しているとまるでアイコンそのものが発言しているかのような気がしてくるから困る。
アニメキャラなアイコンなんかが特にひどい。
どうもアイコンに対する印象がそのままその人の印象になりかねなくて、利発そうなキャラのアイコンにしている人は利発なんだろうなといつの間にか思ってたりする。
特に発言数の多い人はそう思われてる傾向が高いんじゃないだろうか。
メッセンジャーだと基本1対1なのであまりそういう気にはならないんだけど、twitterはちょっとひどすぎる。


ということで、モテたい人は見た目モテそうなアイコンにしておけばいいんじゃないかな。
逆に異性キャラのアイコンは超危険!?
有名人など実在する他人の写真はちょっと例外っぽくてよく分からない。
英数字のみで構成されるアイコンは超無機質で、妙な印象を持つことはまず少ない。
動物なアイコンも意外に無機質で、何故かあまり印象に残らない。
自身の顔写真はある意味正解、ある意味不正解、でもきっと一番無難。
ちょっとこの辺、みんながどう感じてるか知りたい。


ちなみにちょっと前までは娘に大仏マスクをかぶせた写真をアイコンにしてたんだけど、大仏顔があまりにアレだったので今は飼い猫の写真。
当時どういう風に見えてたんだろうか・・・

2007年09月15日

爆発連鎖ゲーム「CHAIN DETONATION」

AS3でゲーム作っている方々に挙がっているゲームのあまりの少なさにビックリしていたところに、ActionScript3.0がゲーム開発者に広がらないというABAさんのエントリを読んで「こりゃ作っていかないとどうにもならないな」と一念発起、ゲームを作ろうぜブームが爆発して広がって欲しいなと願いを込めてちょっくら作ってみました。

 
CHAIN DETONATION

基本的には爆発連鎖ゲーで、そこにちょっとだけマインスイーパの要素を含めてみた感じです。
一応 ActionScript3.0 で作ってはみたものの、プロトタイプは ActionScript1.0 で作っていたのと処理負荷が描画にほとんど持ってかれちゃってるのとで、ActionScript3.0で作る必要性があまり感じられない作品になっちゃいました。
というかこの手のミニゲームはルール作りが全てだと思います。

もう少し工夫すれば描画負荷は下げられそうですが、とにかく早く遊べるものにしてしまいたかったので自分的には完成度5割です。
詰め問題モードやらリアルタイム連鎖モードなど、いろいろ考えてはいるのでこれからの展開にご期待ください。


ところで今頃になって1年半前に作ったゲーム「桜吹雪」を一度もブログで紹介していなかったことに気付く。
もう少し陽の目を浴びてもいいだろうとは思ってたけど迂闊・・・せっかくなのでバージョンアップしよう。

2007年09月13日

JSFLで塗りの回転ができない問題が若干解決

Flash のグラデーション拡張機能 GradationAssist をアップデート。
塗りの回転が数値指定できるようになりました。

GradationAssist

JSFLからは塗りの変形が反映されない仕様はどうしようもなかったので、塗られるシェイプの方を逆回転させて塗り直して元に戻すという逆転の発想ですw
シェイプの形によっては塗りがおかしくなるかもしれませんが、どこまで対応できるか分からないので自己責任でお使いください。
残念ながら、放射状グラデーションには対応していません。

詳しくはインスコ時に出てくるソースをご参照あれ。


関連エントリ:グラデーション拡張機能作ったよ