BitmapDataを扱えるカスタムAssetManager


作りました。。(継承でも内部利用でもなく)Starling2.xのAssetManagerをカスタム化しています。

  • 他のAPIと同じ感じでBitmapDataを取得可能
  • Texture生成前にBitmapDataに加工処理を挟める
  • Texture生成時にBitmapDataを破棄するか、保持するか選べる
  • コンテキストのロストにも自動対応

です。このカスタム化されたAssetManagerのソースはここにあります。


サンプル動作の様子です。上から、読み込みBitmapData保持&Texture作成、読み込みBitmapData破棄&Texture作成+前処理でブラーフィルタ、読み込みBitmapData保持&Texture作成+前処理でDisplacementMapフィルタ適用、をしています。フィルタはStarling上でかけているのでなく、native側(Flash8からあるFilter)の処理です。すでに効果がかかったテクスチャをそのままImage(Quad)として表示しています。1つのpng画像を3回読み込んで、別の名前でAssetManagerに登録しています。サンプルのソースはここにあります。

基本的な使い方

通常のAssetManagerの機能はすべて持っています。追加のAPIに

sample.as

/* BitmapDataを登録する(通常使わない) */
function addBitmapData(name:String, bmd:BitmapData):void;

/* BitmapDataを登録破棄する(通常使わない) */
function removeBitmapData(name:String, dispose:Boolean=true):void;

/* BitmapDataを得る */
function getBitmapData(name:String):BitmapData;

/* 登録されているBitmapDataの名前一覧を得る */
getBitmapDataNames(prefix:String="", out:Vector.<String>=null):Vector.<String>;

/* 登録されているBitmapData一覧を得る */
function getBitmapDatas(prefix:String="", out:Vector.<BitmapData>=null):Vector.<BitmapData>;

が、あります。
BitmapDataをAssetManagerに保持するためには下記のように、テクスチャ生成前のフック関数を設定し、その中でaddBitmapDataする必要があります。
(最初の投稿後、戻り値の仕様が変わっています。コメント欄参照。)

sample.as
_assetManager.enqueue('hoge.png');
 // テクスチャ生成前のフック関数を設定
_assetManager.setBeforeTextureCreationCallback(onBeforeTextureCreation);
//ロード開始
_assetManager.loadQueue(function(ratio:Number):void{});


// テクスチャ生成前のフック関数 登録名と読み込んだBitmapDataが引数で呼び出される
funtion onBeforeTextureCreation(name:String, bmd:BitmapData):BitmapData {

  // すべての外部Bitmapロード時に呼ばれるので、名前で分岐する
  if(name=="hoge") {
    // 保持する場合このタイミングでBitmapDataを登録する。
    _assetManager.addBitmapData(name, bmd);
    // テクスチャを生成するBitmapDataを返す(通常引数のbmdをそのまま返す)
    return bmd; 
  }
}

全てのBitmapDataを保持する場合は、下記のようにします。

sample.as
_assetManager.setBeforeTextureCreationCallback(
  function(name:String, bmd:BitmapData):BitmapData{
    _assetManager.addBitmapData(name, bmd);
    return bmd;
  }
);

このタイミングでBitmapDataを編集することも可能です。

sample.as
if(name=="hoge") {
  var bFilter:BlurFilter = new BlurFilter(); // ぼかしをかけている例
  bmd.applyFilter(bmd, bmd.rect, bmd.rect.topLeft,
  return bmd; 
}

ちなみに戻り値にnullを返すと、引数のbmdをそのまま返すのと同じ意味となります。
なんのために使うのかというと、引数とは別のBitmapDataでテクスチャを生成する場合に使います。下記はBitmapDataを半分の大きさに加工した例です。(リサイズには別のBitmapDataが必要になります。)

sample.as
if(name=="hoge") {
  var harfMatrix:Matrix = new Matrix();
  harfMatrix.scale(0.5, 0.5);
  var bmd2:BitmapData = new BitmapData(bmd.width*0.5, bmd.height*0.5, bmd.transparent);
  bmd2.draw(bmd, harfMatrix, null, null, null, false);
  return bmd2; 
}

コンテキストロスト時の挙動

BitmapDataを保持していない場合

自動で外部からの画像再ロード処理がかかり、読み込み完了後にフック関数が再度呼ばれます。その後、テクスチャ再アップロードが行われます。

BitmapDataを保持している場合

外部からの画像再ロード処理はかかりませんが、保持しているビットマップよりテクスチャ再アップロードが自動で行われます。

そもそもフック関数を設定していない場合

通常のAssetManagerと同じ挙動をします。自動で画像再ロード処理がかかり、テクスチャ再アップロードが行われます。

この3パターンをまとめると、良きように動作するので結局何もしなくてよいです。Bitmapの編集を行っている場合は、そこで再度の負荷がかかる事だけ頭に入れてください。

作成の経緯

StarlingのAssetManagerは画像を読み込むと自動でTextureを作成してくれるのですが、その際にメモリ削減のためにBitmapDataを破棄してしまいます。しかしBitmapDataを後に参照したい状況も多々あるので、(Spine使う時とか、BitmapDataを編集したい時とか)それができるAssetManagerを作りました。

最初はなんとかStarlingのAssetManagerを継承して作ろうとしたものの、外部から参照できないインナー関数が多く、ほとんどダブル実装になってしまうので、あきらめました。次に内部にAssetManagerを持って利用するうすいラッパーのようなビットマップローダーを作りかけましたが、同じような理由で諦めました。。StarlingのAssetManagerは責務が多く、AssetのStorageとLoderとでクラスを分けるべきではないかと(内部処理だけでもいいので)思う今日この頃。

Starling1.xでも動くかもしれません。どうだろ。需要があればそのうち作ります。

その他

このタコイカ家族は2年ほど前に筆者がクリエイターLineスタンプ申請用に描きかけてたやつです。作業が進まず日の目をみないのでここのサンプルで出していく事にしました!