トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

高速な描画領域(BitmapData)_Flex

高速な描画領域(BitmapData)

ゲームなどのようにリサイズされない画像を使用する場合には、ダイレクトに画像にアクセスしたほうがBitmapよりも高速に処理できます。

Flexでは「BitmapData」というものを扱うことで、高速な画像アクセスが可能になります。

256 x 256ピクセルの描画領域を作成する例

package {
   import flash.display.*;
   import flash.text.*;
   import flash.utils.*;
   import flash.geom.*;

   public class BitmapDataTest extends Sprite {
       private var m_bmp:Bitmap;
       private var m_bmpD:BitmapData;
       
       public function BitmapDataTest() {
           var stTim:int, endTim:int;
           var x:int, y:int;
           var rr:int, gg:int, bb:int;
           
           // テキストフィールドの作成
           var m_Text:TextField = new TextField();
           m_Text.x = 0;
           m_Text.y = 270;
           addChild(m_Text);       // シーンに追加
           
           // BitmapDataの作成(引数は幅と高さとアルファ有効フラグ)
           m_bmpD = new BitmapData(256, 256, false, 0xffffff);

           // Bitmapの作成
           m_bmp = new Bitmap(m_bmpD);
           addChild(m_bmp);            // シーンに追加

           // 256 x 256の領域を塗りつぶす例
           stTim = getTimer();

           for(y = 0; y < 256; y++) {
               for(x = 0; x < 256; x++) {
                   rr = x;
                   gg = y;
                   bb = 80;
                   m_bmpD.setPixel(x, y, (rr << 16) | (gg << 8) | bb);
               }
           }

           endTim = getTimer();

           m_Text.textColor = 0xff0000;
           m_Text.text = "描画時間 : " + String(endTim - stTim) + " ms";
       }
   }
}

これを実行すると以下のようになります。

BitmapDataを扱う

m_bmpD = new BitmapData(256, 256, false, 0xffffff); 
m_bmp  = new Bitmap(m_bmpD);

BitmapDataをnewするときの引数は第一引数から順に、画像の幅、画像の高さ、アルファ値を有効にするか、デフォルトのクリア色、になります。

Bitmapを作成するときに、作成したBitmapDataを第一引数に入れることで、後は普通のBitmapと同様にSpriteやMovieClipに貼り付けることができます(画像へのアクセスはBitmapData経由で行う)。

BitmapData上で点を打つ

m_bmpD.setPixel(x, y, (rr << 16) | (gg << 8) | bb);

にて、BitmapDataの(x, y)の位置に、RGB(rr, gg, bb)の点を打ちます。ここでの変更は、親であるBitmapにそのまま反映されることになります。

BitmapDataを使ってBitmapの画像を描画

Spriteを重ね合わせるよりも、BitmapDataを持つ画像同士で直接描画したほうが高速に処理できます。これは、BitmapDataの「copyPixels」を使用します。

package {
   import flash.display.*;
   import flash.text.*;
   import flash.utils.*;
   import flash.events.*;  
   import flash.geom.*;

   public class BitmapDataTest2 extends Sprite {
       private var m_Text:TextField;

       // 読み込む外部画像を指定 ==> Image0
       [Embed(source='teapot.png')]
       private var ImageTeapot:Class;
       
       private var m_backBmpD:BitmapData;      ///< 描画領域のBitmapData
       private var m_teapotBmp:Bitmap;         ///< ティーポット画像のBitmap
   
       private var m_backRect:Rectangle;       ///< 描画領域の範囲
   
       public function BitmapDataTest2() {
           // テキストフィールドの作成
           m_Text = new TextField();
           m_Text.x        = 0;
           m_Text.y        = 270;
           m_Text.autoSize = "left";
           addChild(m_Text);       // シーンに追加

           // ティーポット画像の生成
           m_teapotBmp = new ImageTeapot();

           // スクリーンの範囲
           m_backRect = new Rectangle(0, 0, 256, 256);
           
           // 描画領域用画像の生成
           m_backBmpD = new BitmapData(256, 256, false, 0xffffff);

           // 256x256ピクセルのBitmapをシーンに追加
           addChild(new Bitmap(m_backBmpD));

           // タイマーの追加
           var timer:Timer = new Timer(50, 0);     // 50 ms ごと
           timer.addEventListener(TimerEvent.TIMER, onTimer);
           timer.start();
       }

   
       /**
        * タイマーイベント
        */
       private function onTimer(evt:TimerEvent):void {
           var stTim:int, endTim:int;
           var rr:int, gg:int, bb:int;
           var bmp:Bitmap;
           var x:int, y:int, xx:int, yy:int;

           var srcRec:Rectangle = new Rectangle(0, 0, 48, 48);
           var pos:Point = new Point();
           
           stTim = getTimer();

           // 背景を指定色でクリア
           rr = 32;
           gg = 10;
           bb = 60;
           m_backBmpD.fillRect(m_backRect, 0x00000000 | (rr << 16) | (gg << 8) | bb);

           // キャラクタの描画
           yy = 0;
           for(y = 0; y < 10; y++) {
               xx = 0;
               for(x = 0; x < 12; x++) {
                   pos.x = xx;
                   pos.y = yy;
                   m_backBmpD.copyPixels(m_teapotBmp.bitmapData, srcRec, pos);
                   xx += 32;
               }
               yy += 32;
           }

           endTim = getTimer();
           
           m_Text.text = "BitmapData.copyPixels 描画時間 : " + String(endTim - stTim) + " ms";
       }
   } 
}

上記は、「teapot.png」というアルファ付きの48x48ピクセルの画像を、256x256のBitmapData領域に描画している例です。

実行すると、以下のようになります。わざと重なるように48x48ピクセル画像を32x32ずつずらしながら描画しています。アルファによる透過処理もできているのがわかります。

タイマーイベントで呼ばれるonTimer関数内で、

  • 背景を指定色でクリア
  • BitmapDataにcopyPixelsにて描画

を行っています。

BitmapDataの背景を指定色でクリア

var m_backRect:Rectangle = new Rectangle(0, 0, 256, 256);
m_backBmpD.fillRect(m_backRect, 0x00000000 | (rr << 16) | (gg << 8) | bb);

BitmapDataのサイズはあらかじめわかっているので、その領域をRectangleであらわしてBitmapData.fillRectの第一引数に入れます。第二引数では塗りつぶし色を0x00RRGGBBの順で16進数で指定します。

copyPixelsでBitmapをBitmapDataに描画

m_backBmpD.copyPixels(m_teapotBmp.bitmapData, srcRec, pos);

第一引数ではBitmap内のBitmapDataを渡しています。第二引数では描画元のteapotBmpのRectangle、第三引数は

var pos:Point = new Point();

として定義されたPoint型の位置情報を渡します。「new」は負荷がかかる処理となりますので、ループの外にてあらかじめ作成しておくほうがよいです。

copyPixelsの処理は速いがゆえにほとんど負荷がかかっていないようにみえます。ゲームのような描画速度命の処理では、BitmapDataを使って

  • 背景クリア
  • モブキャラ描画
  • メインキャラ描画
  • 前景キャラ描画
  • ゲージやスコアなどの描画

のように重ねて重ねていくとそれっぽくなりそうです。

Future's Laboratory 技術格納庫 2004-2013 Yutaka Yoshisaka.