ActionScript3.0 アルファ値のビット演算

レンダリングの公式について、α値が意図した通りにならないことがあります。

for (var i = 0; i <= 0xff; i++) {
	var color32 = i << 24 | 0x10 << 16 | 0x20 << 8 | 0x30;
	var va = color32 >> 24;
	var vr = color32 >> 16 & 0xFF;
	var vg = color32 >> 8 & 0xFF;
	var vb = color32 & 0xFF;
	trace( color32.toString(16) + ":" + i.toString(16) + ":" + va.toString(16) + "," + vr.toString(16) + "," + vg.toString(16) + "," + vb.toString(16) );
}

実行結果は以下の通り。

00102030:0:0,10,20,30
01102030:1:1,10,20,30
02102030:2:2,10,20,30

7f102030:7f:7f,10,20,30
-7fefdfd0:80:-80,10,20,30
-7eefdfd0:81:-7f,10,20,30

-1efdfd0:fe:-2,10,20,30
-efdfd0:ff:-1,10,20,30

まず最初のα,r,g,bを表す32bit値が符号付きになってしまっています。これは演算結果自体は正しいのですが、toString()で変換する時に符号付きの数値だと判断されているだけですから、あまり問題ではないと思います。それでも気になるのであれば、変数宣言時にuint型として指定すればいいようです。

var color32:uint = i << 24 | 0x10 << 16 | 0x20 << 8 | 0x30;

次に気になるのが、32bitのカラーコードから、α,r,g,bを切り出すところで、αが0×80を超えたところから符号付きになってしまっています。じゃあこれも同様にuintにすればいいのかというと…

var va:uint = color32 >> 24;

結果は

7e102030:7e:7e,10,20,30
7f102030:7f:7f,10,20,30
80102030:80:ffffff80,10,20,30
81102030:81:ffffff81,10,20,30

うまくいきません。これはActionScriptのマニュアルで“ビット単位シフト演算子”の項目を読むとわかるのですが、>>は、ビット単位の右シフト、>>>は、ビット単位の符号なし右シフトと書かれています。読み替えると、>2個は符号付きの右シフトということなので、32ビット値を符号付きで右シフトすると最上位ビットが残ってしまうのですね。

「符号付きの右シフトって何?」という場合は検索してください。

α値を取り出す箇所は以下のようになります。

var va = color32 >>> 24;

別の方法として、

var va = (color32 >> 24) & 0xff;

とすることもできますが、シフト+マスクの2演算よりも、符号無し右シフト1回の方が処理が速いと思います。

・・・ここまで書いていてふと思ったのですが、よく、ActionScriptの解説系サイトで、2の累乗で割る時は右シフト(>>)を使いましょうと書かれている事があるのですけど、値が0×80000000よりも大きい場合には符号がひっくり返るんじゃないでしょうか。

var a:Number = 2147483649; // = 0x80000001
var b:Number = a / 2;
var c:Number = a >> 1;
var d:Number = a >>> 1;
var e:Number = a + c;
var f:Number = a + d;
trace("a = " + a);
trace("b(a/2) = " + b);
trace("c(a>>1) = " + c);
trace("d(a>>>1) = " + d);
trace("e(a+c) = " + e);
trace("f(a+d) = " + f);

実行結果は

a = 2147483649
b(a/2) = 1073741824.5
c(a>>1) = -1073741824
d(a>>>1) = 1073741824
e(a+c) = 1073741825
f(a+d) = 3221225473

やはり、マイナスになりますね。
わかっていて計算している分にはいいですし、こんな大きな値を使う事ってあまりないのでほとんど問題にならないと思いますが。だからといって、全部が全部、>>の代わりに>>>を使って計算すればいいのかというとそうではないのですね。

var g:Number = -100;
trace(g);
trace(g >> 1);
trace(g >>> 1);


-100
-50
2147483598

さてと、じゃあちょっと今から、838861枚のコインを買ってきますね。

コメントは受け付けていません。