マスク処理や重ね合わせについて

二つの数値を合成したい場合、たとえば、あるワードサイズの変数の上位バイトと、他の変数の下位バイトを一つのワードサイズにまとめたい場合、以下のような処理をする。

w = (w0 & 0xff00) | (w1 & 0x00ff);

dw = (dw0 & 0xff00ff00) | (dw1 & 0x00ff00ff);

また、ゲームなどで使われる、背景とその上に重ねられる画像の合成では、以下のような処理を見かける。

// C キャラクターのピクセルデータ
// M マスクデータ
// B 背景のピクセルデータ
// D 書き込むピクセルデータ

D = (C & M) | (B & ~M)

 

最適化

これらの演算はどちらも、(C & M) | (B & ~M) = ((C ^ B) & M) ^ B と最適化できる。Cライクに書けば、以下のようになる。

w = w0 ^ w1;
w &= 0x00ff;
w ^= w0;

dw = dw0 ^ dw1;
dw &= 0x00ff00ff;
dw ^= dw0;

D = C ^ B;
D &= M;
D ^= B;

このようにすることで、マスクパターンを減らし、余分な演算をなくすことができる。

 

証明

(C & M) | (B & ~M)

 = (C & M) ^ (B & ~M)
 = (C & M) ^ (B & M) ^ B
分配法則、A & (B ^ C) = (A & B) ^ (A & C)より、
 = ((C ^ B) & M) ^ B

証明終わり。

 

余談

MMXテクノロジ命令ではサチュレーション付き減算ができるため、以下のようなマスク処理も可能である。

// マスク用にレジスタを余分に使ってしまう例
pcmpeqb mm2, mm2  
psrlw mm2, 8 // mm2 = 00ff00ff00ff00ff for mask
pcmpeqb mm3, mm3  
psllw mm3, 8 // mm3 = ff00ff00ff00ff00 for mask
     
pand mm0, mm2  
pand mm1, mm3  
por mm0, mm1  
// マスク用レジスタを最小限にしたコード
pcmpeqb mm2, mm2  
psrlw mm2, 8 // mm2 = 00ff00ff00ff00ff for mask
     
pand mm0, mm2  
psubusb mm1, mm2 // mask!
por mm0, mm1  

MMXテクノロジ命令を使ったコーディングでは、カラーキーの処理をするときなどにpcmpeqwなどで動的にマスクを生成することもあるが、そのときにもこのテクニックは役に立つ。

数少ないMMXレジスタは、少しでも有効に使いたいものです。