[ヘルプ]の[モバイル]/[Flash Platformのパフォーマンスの最適化]/[ActionScript 3.0のパフォーマンス]/[ピクセルの操作]には、メソッドBitmapData.setPixel()とBitmapData.setVector()を用いたピクセル操作の最適化が説明されています。そこでwonderflに投稿したテスト用のコードで、3つのやり方について速さを比べてみました。
実際のコードそのものはwonderflでご覧いただけますので、ここではフレームアクションに直して簡単にご説明します。なお、ビットマップのインスタンスを動的につくって配置する方法については、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第34回「3次元空間における回転」2ページ「ビットマップのインスタンスを動的に配置する」をお読みください。
ひとつ目は、BitmapData.setPixel()メソッドで、ピクセルごとに単純に塗った場合です。メソッドには初めのふたつの引数でxy座標、そして3つ目にRGBカラーを渡します。
BitmapDataオブジェクト.setPixel(x座標:int, y座標:int, RGBカラー:uint):void
以下のスクリプト001は、ステージ一杯の大きさのBitmapDataオブジェクトをBitmapインスタンスに入れて配置し、青(0x0000FF)から赤(0xFF0000)のグラデーションで塗ります(図001)。初めのforループは垂直方向に赤成分を0から255まで増やし、入れ子のforループで水平方向に青成分を0から255に高めます。なお、カラー値に0.5を加えているのは、最大値の端数が切捨てられて255に達しないことを避ける四捨五入の処理です。
図001■ グラデーションは右方向に青・下方向に赤を高める
var nWidth:int = stage.stageWidth; var nHeight:int = stage.stageHeight; var myBitmapData:BitmapData = new BitmapData(nWidth, nHeight, false); var myBitmap:Bitmap = new Bitmap(myBitmapData); addChild(myBitmap); // myBitmapData.lock(); for (var i:uint = 0; i < nHeight; i++) { var pixelY:uint = i / nHeight * 256 + 0.5; for (var j:uint = 0; j < nWidth; j++) { var pixelX:uint = j / nWidth * 256 + 0.5; var color:uint = pixelY << 16 | pixelX myBitmapData.setPixel(j, i, color); } } // myBitmapData.unlock();
BitmapDataオブジェクトのピクセルを塗ると、それを参照するBitmapインスタンスのデータが更新されます。しかし、フレームアクションが済むまでインスタンスのイメージはステージに描画されません。いちいちデータを反映するのは無駄です。
そこで、forループに入る前にBitmapData.lock()メソッドを呼出すと、BitmapDataオブジェクトの書替えは、Bitmapインスタンスに反映されません。そして、ピクセルごとの塗りを終えてforループから抜けたとき、BitmapData.unlock()メソッドでデータを更新すればよいのです。前掲スクリプト001でコメントアウトされている2行のステートメントを有効にすると、その結果が確かめられます。これが、ふたつ目のやり方です。
3つ目は、BitmapData.setVector()メソッドでピクセルを塗ります。このメソッドはピクセルごとのカラーをuintベース型のVectorオブジェクトに納めて、引数に渡します。
BitmapDataオブジェクト.setVector(矩形領域Rectangleオブジェクト:Rectangle, (A)RGBカラーVectorオブジェクト:Vector.):void
つまり、BitmapDataオブジェクトを塗るのはBitmapData.setVector()メソッド1回の呼出しで一気に行います。そのため、Bitmapインスタンスのデータの更新を止めるという必要がありません。このBitmapData.setVector()メソッドを用いたのが、つぎのスクリプト002です。
スクリプト002■ BitmapData.setVector()メソッドでグラデーションの塗りを設定するvar nWidth:int = stage.stageWidth; var nHeight:int = stage.stageHeight; var myBitmapData:BitmapData = new BitmapData(nWidth, nHeight, false); var myBitmap:Bitmap = new Bitmap(myBitmapData); var size:uint = nWidth * nHeight; var pixels:Vector.<uint> = new Vector.<uint>(size); addChild(myBitmap); for (var i:uint = 0; i < size; i++) { var pixelX:uint = (i / nWidth * 256 + 0.5) % 256; var pixelY:uint = int(i / nWidth) / nHeight * 256 + 0.5; pixels[i] = pixelY << 16 | pixelX; } myBitmapData.setVector(myBitmapData.rect, pixels);
なお、BitmapDataオブジェクトをコンストラクタメソッドの呼出しでつくるとき、第3引数に不透明となるfalseを渡しています。この場合、BitmapData.setVector()メソッドの第2引数とするVectorオブジェクトには、RGBカラーを整数エレメントに納めればよいです。
もし、BitmapDataオブジェクトを透明に(第3引数をtrueもしくは省略)したときは、ARGBカラーをVectorオブジェクトのエレメントとして加えなければなりません。