「マスク (情報工学)」の版間の差分
編集の要約なし |
|||
47行目: | 47行目: | ||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||
このような引数を持つ関数の内部では、個々のビットを取出すのに <code>AND</code> 演算を行っている。例えば、<code>glClear()</code> の実装は次のようになる。 |
このような引数を持つ関数の内部では、個々のビットを取出すのに <code>AND</code> 演算を行っている。例えば、<code>glClear()</code> の実装は次のようになる。 |
||
< |
<syntaxhighlight lang="c"> |
||
void glClear(GLbitfield bits) { |
void glClear(GLbitfield bits) { |
||
if (bits & GL_COLOR_BUFFER_BIT) { |
if (bits & GL_COLOR_BUFFER_BIT) { |
||
61行目: | 61行目: | ||
// stencil バッファをクリア |
// stencil バッファをクリア |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
この手法の利点は、引数の個数を削減することでオーバーヘッドを削減できる点である。データとして受け渡しできる最小単位は1バイトなので、それぞれの操作を別々の引数で受け渡すと、引数当たり7ビットが無駄となり、余分なスタック領域を占有することになる。実際には引数は32ビットの整数であることが多く、その場合は32個のオプションを1つの引数で指定できる。しかし、この手法を単純に実装すると[[型安全性|型安全]]ではなくなる。<code>GLbitfield</code> は単純に <code>unsigned int</code> で定義されているので、コンパイラは <code>glClear(42)</code> や <code>glClear(GL_POINTS)</code> といった無意味な呼び出しがあってもエラーを検出できない。[[C++]]では、glClear が受け取る引数群をクラスとしてカプセル化でき、型安全性を保証できる(外部リンク参照)。 |
この手法の利点は、引数の個数を削減することでオーバーヘッドを削減できる点である。データとして受け渡しできる最小単位は1バイトなので、それぞれの操作を別々の引数で受け渡すと、引数当たり7ビットが無駄となり、余分なスタック領域を占有することになる。実際には引数は32ビットの整数であることが多く、その場合は32個のオプションを1つの引数で指定できる。しかし、この手法を単純に実装すると[[型安全性|型安全]]ではなくなる。<code>GLbitfield</code> は単純に <code>unsigned int</code> で定義されているので、コンパイラは <code>glClear(42)</code> や <code>glClear(GL_POINTS)</code> といった無意味な呼び出しがあってもエラーを検出できない。[[C++]]では、glClear が受け取る引数群をクラスとしてカプセル化でき、型安全性を保証できる(外部リンク参照)。 |
||
2020年7月5日 (日) 23:01時点における版
コンピュータでの演算でマスク(英: mask)とは、ビット演算と呼ぶビット単位の操作を行う処理である。ビットマスク (bit mask) とも。
マスクを使用すると、バイト、ニブル、ワードなどの複数のビットを同時にオンやオフにしたり、ビット単位にオン/オフを反転させたり、また特定のビットの状態を知ることができる。注目するビットを操作し、それ以外のビットを操作対象外として覆うことから、顔の口に当てる「マスク」などのようにマスクと呼ぶ。
むしろそのように「使用すると何かができる」ということよりも、「使用しなければならない」という場合のほうが多い。一般に、ほとんどのコンピュータの命令セットは、レジスタの一部のビットのみ、あるいは、メモリの一部のビットのみを操作する、というような命令を持たない(少ないが、持っているものもある)。したがってレジスタやメモリ中の一部のビットのみを操作したい、という場合は、ビットマスクで論理ビット演算(bitwise operation)を行わねばならないのである。メモリが対象の場合は、さらに「リード・モディファイ・ライト」が必要な場合も多い。
ビットマスク
ビットを 1
にマスクする
特定のビットだけをオンにするには、ビット単位のOR
演算を行う。Y OR 1 = 1
であり、Y OR 0 = Y
となるORのビット演算となる。したがって、ビットを確実にオンにするには、1
との OR
を行えばよい。変更を加えないビット位置では、0
との OR
を行う。
例: LSBから4番目のビットをオンにする
10011101 10010101 マスクされる2バイト OR 00001000 00001000 マスクする2バイト = 10011101 10011101 マスクされた結果
ビットを 0
にマスクする
OR
演算ではオンのビットをオフにすることはできない。その場合はビット単位のAND
を使う。1
との AND
を行うと、値は単に元のままとなるので、Y AND 1 = Y
である。しかし、0
との AND
を行うと、必ず 0
になり Y AND 0 = 0
である。変更しないビット位置では、1
との AND
を行えばよい。
例: LSBから4番目のビットをオフにする
10011101 10010101 マスクされる2バイト AND 11110111 11110111 マスクする2バイト = 10010101 10010101 マスクされた結果
個々のビットの状態を調べる
ビットマスクを使うと、特定のビットに注目して取り出しその状態を調べることができる。そのためには、ビット単位の AND
を行って対象の特定ビット以外の状態に拘わらず全てオフにすればよいので、上述したように対象外のビット位置は 0
との AND
にすればよい。こうして得られた値を 0
と比較し、等しければ(0
ならば)そのビットはオフと分かるし、等しくなければオンだと分かる。このとき具体的にどういう値かを考慮する必要は無く、単に 0
かどうかだけで判断できる。
例: LSBから4番目のビットの状態を調べる
10011101 10010101 マスクされる2バイト AND 00001000 00001000 注目するビットのみ1としたマスクする2バイト = 00001000 00000000 マスクされた結果
ビット値の反転
ここまでは、ビットをオンにする方法とオフにする方法を解説してきたが、両方を同時に行うにはどうすればよいだろうか。元の値がどうであろうと問題ではなく、単にビット列を反転させたい場合もある。この場合、XOR
演算を使う。XOR
は、演算対象の相互のビット(通常は2つ)のうちいずれかのビットのみが 1
の場合に 1
を返す。つまり、2つのビットが共に 1
なら 0
を返す。また両方が 0
なら 0
を返す。結果として、対象とする相互のビットの状態が以前から反転した異なる状態に変えることができる。したがって、反転させたいビット列について全て 1
とのビット単位の XOR
を行えば、元のビット列を反転したビット列が得られる。元のビットが 1
なら 1 XOR 1 = 0
となるし、元のビットが 0
なら 0 XOR 1 = 1
となる。
例: すべてのビットを反転する
10011101 10010101 反転前の2バイト XOR 11111111 11111111 反転させるため全て1の2バイト = 01100010 01101010 反転された結果
ビットマスクの使用例
関数の引数
C言語のようなプログラミング言語では、ビットマスクは関数のブーリアン型の引数を一連の名前付きの値として渡すのに使える。例えば、グラフィックスAPIであるOpenGLには、glClear()
というコマンドがある。これは画面や他のバッファをクリアするもので、4つのバッファ(color、depth、accumulation、stencil)をクリアできるので、これらを指定するのにブーリアン型の引数を4つ使ったAPIが考えられる。すると、その呼び出しは次のようになる。
glClear(1,1,0,0); // 実際の glClear とは異なる。
これでは、可読性が低く、何をしたいのかわかりにくい。そこで実際には特定ビット位置をオンにしたビットマスクに GL_COLOR_BUFFER_BIT
、GL_DEPTH_BUFFER_BIT
、GL_ACCUM_BUFFER_BIT
、GL_STENCIL_BUFFER_BIT
と名前を付け、glClear()
を次のように宣言している。
void glClear(GLbitfield bits);
すると、この関数の呼び出しは次のようになる。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
このような引数を持つ関数の内部では、個々のビットを取出すのに AND
演算を行っている。例えば、glClear()
の実装は次のようになる。
void glClear(GLbitfield bits) {
if (bits & GL_COLOR_BUFFER_BIT) {
// color バッファをクリア
}
if (bits & GL_DEPTH_BUFFER_BIT) {
// depth バッファをクリア
}
if (bits & GL_ACCUM_BUFFER_BIT) {
// accumulation バッファをクリア
}
if (bits & GL_STENCIL_BUFFER_BIT) {
// stencil バッファをクリア
}
}
この手法の利点は、引数の個数を削減することでオーバーヘッドを削減できる点である。データとして受け渡しできる最小単位は1バイトなので、それぞれの操作を別々の引数で受け渡すと、引数当たり7ビットが無駄となり、余分なスタック領域を占有することになる。実際には引数は32ビットの整数であることが多く、その場合は32個のオプションを1つの引数で指定できる。しかし、この手法を単純に実装すると型安全ではなくなる。GLbitfield
は単純に unsigned int
で定義されているので、コンパイラは glClear(42)
や glClear(GL_POINTS)
といった無意味な呼び出しがあってもエラーを検出できない。C++では、glClear が受け取る引数群をクラスとしてカプセル化でき、型安全性を保証できる(外部リンク参照)。
2の冪の剰余
剰余を求める計算はビット演算より一般的に低速である。法が2の冪(2^n)のときは、単純に 2^n - 1
でビットマスクをとるだけでよい。例:
13 MOD 4 = 1 (13 を 4 で割った余りは 1 に等しい) 1101 AND 0011 = 0001 (ビットマスクによる剰余)
実用上の例では、ハッシュテーブルのためのハッシュ関数が挙げられる。ハッシュ関数には大きな定義域の関数がよく使われるが、ハッシュ値からハッシュテーブルのインデックスを得るには、合同式の操作を行って配列のサイズに収まるようにする必要がある。配列のサイズを2の冪にしておけば、この剰余演算を高速化できる。
IPアドレスのマスク
IPアドレスのマスクは IP ACL (アクセス制御リスト)で許可されるものと拒否されるものを指定するのに使われる。インタフェース上のIPアドレスを設定するマスクは、先頭が255で左側の方が大きい値になる。例えば、IPアドレス 209.165.202.129 に対して 255.255.255.224 というマスクが対応する。IP ACL のマスクはその逆で、例えば 0.0.0.255 となる。これを逆マスク (inverse mask) あるいはワイルドカードマスク (wildcard mask) と呼ぶこともある。このマスク値をバイナリ(0と1)で表すと、アドレスのビット列のうちどの部分を処理するかが示されている。0となっているビット位置のアドレスビットは考慮され(一致する必要がある)、1となっているビット位置のアドレスビットは考慮しない。
マスクの例:
(処理すべきトラフィックの)ネットワークアドレス 10.1.1.0 マスク 0.0.0.255
ネットワークアドレス(バイナリ) 00001010.00000001.00000001.00000000 マスク(バイナリ) 00000000.00000000.00000000.11111111
バイナリ形式のマスクを見ると、先頭3オクテットは指定されたバイナリ形式のネットワークアドレス (00001010.00000001.00000001) と正確に一致しなければならない。最後尾にオクテットは考慮する必要はない (.11111111)。したがって、10.1.1. で始まるアドレスの全トラフィックを対象とし、最後尾のオクテットは考慮しない。つまり、このマスクでは 10.1.1.1 から 10.1.1.255 までのアドレスを処理する。
255.255.255.255 から通常のマスクを引き算すると ACL 用逆マスクが得られる。例えば、ネットワークアドレス 172.16.1.0 の通常のマスクが 255.255.255.0 の場合、逆マスクは次のように求められる。
255.255.255.255 - 255.255.255.0 (通常のマスク) = 0.0.0.255 (逆マスク)
アドレス 0.0.0.0 で逆マスクが 255.255.255.255 なら、アドレス範囲を指定しないことを示している。アドレス 10.1.1.2 で逆マスクが 0.0.0.0 なら唯一のホスト 10.1.1.2 だけを指す。
画像マスク
最も「マスク」らしく、またその意味が視覚的にわかりやすい応用である。
コンピュータグラフィックスにおいて、ある画像を何らかの背景画像の上に配置したい場合、背景画像を透過させる領域をマスクで指定する。この場合、配置対象の画像には2つのビットマップが存在する。画像内の描画しないピクセル(透明部分)に対応するビットを全て0にしたもの(実際の画像)と、描画する部分のピクセルを全て0とし、描画しない部分(透明部分)を全て1にした「マスク」である。右図の例で、黒いピクセルは全ビットが0で、白のピクセルは全ビットが1である。
このような画像を背景画像上に配置する場合、まず画面上のピクセルと画像マスクをビット単位のAND操作で合成する。すると、透明部分は背景がそのまま残り、画像を描画したい部分だけが0でクリアされる。
次に、実際の画像と背景画像をビット単位のOR操作で合成する。こうすると、画像のピクセル列は前の操作で背景が消された領域にぴったりと嵌まる。このようにして背景と画像を合成する。
このような技法は、ポインティングデバイスのカーソルの描画、2次元のゲームのキャラクタなどの描画(スプライト)、GUIでのアイコンの描画、動画への文字の表示(スーパー)といった各種画像合成に使われている。
同様の目的の技術として、カラーパレットに透明色を設ける手法や、アルファチャンネルを用いる手法があり、その場合はピクセル画像のビットマスクは不要である。