Windows Mobileでピクセルごとに1つの乗算でのアルファブレンディング

Windows Mobileのグラフィックスに携わっている人は、おそらくGapiDrawグラフィックスライブラリを聞いたことがあるでしょう。 Feature Listを見ると、High Performanceセクションに次の単語があります:「不透明度のあるサーフェスの描画には、ピクセルごとに1回の乗算が必要です」。 つまり、半透明の絵を描くのに必要な乗算はピクセルごとに1回だけであると主張しています。



この記事では、これがどのように可能かを説明します。



まず、Windows Mobileを搭載したほとんどのデバイスには、65,000色の画面があります。 つまり、画面上の各ピクセルに対して、2バイトがメモリに割り当てられます。 次に、このようなデバイスには32ビットプロセッサが搭載されています。 このようなプロセッサでは、2バイトの数値で数学演算を実行する速度は、4バイトの数値で作業する速度とはわずかに異なります。 これが最適化計算の1つです。 グラフィックライブラリは、一度に2ピクセルを処理します。



16ビットピクセルの仕組みを見てみましょう。 上位5ビットは赤用に、次に6ビットは緑用に、残りの5ビットは青用に予約されています。

P = [RRRR RGGG GGGB BBBB] 15 8 7 0
      
      





同時に、色自体は、式に従ってシフトとマスクを使用して取得できます。

 int R = (P >> 11) & 0x1f; int G = (P >> 5) & 0x3f; int B = P & 0x1f;
      
      





ピクセルは、逆変換によって取得されます。

 unsigned short P = (R << 11) + (G << 5) + B;
      
      





透明度のあるピクセルを別のピクセルの上に描画しようとすると、色の混合が発生します。 両方のピクセルは、赤、青、緑の3つのコンポーネントに分かれています。 次に、コンポーネントが混合し、赤が赤と混合し、青が青と混合し、緑が緑と混合します。 そして、取得した値から新しいピクセルがコンパイルされます。



一般に、色の混合は次のようになります。

 C = C1*α + C2*(1 - α)
      
      





C1は、最初のピクセルの赤、緑、または青の値です。

C2は、2番目のピクセルの同じ色の値です。

Cは結果の色です。

α-透明度係数、0〜1の値を取ります。



特にハンドヘルドコンピュータでは、αの実際の値を保存して操作するのは非常に高価なので、整数の範囲に制限されます。 通常、値0〜255が使用されますが、5ビットカラーには5ビットαで十分です。



まず、50%の透明度で色の混合が行われる方法に注意してください。

 C = C1/2 + C2/2
      
      





2による除算は、シフトに置き換えることができます。

 C = (C1 >> 1) + (C2 >> 1)
      
      





次に、2つのピクセルを1対1で混合したときに何が起こるかを見てみましょう。

 P = (R1 >> 1 + R2 >> 1) << 11 + (G1 >> 1 + G2 >> 1) << 5 + (B1 >> 1 + B2 >> 1)
      
      





取得する括弧を展開します。

 P = (P1 & 0xf7de) >> 1 + (P2 & 0xf7de) >> 1
      
      





0xf7deのマスクは、各色成分の1シフトが正しいことを確認するために表示されます。 比較する:

 [RRRR RGGG GGGB BBBB] = P [0RRR RRGG GGGG BBBB] = P >> 1 //         [RRRR RGGG GGGB BBBB] = P [1111 0111 1101 1110] = 0xf7de [RRRR 0GGG GG0B BBB0] = P & 0xf7de //      [0RRR R0GG GGG0 BBBB] = P & 0xf7de >> 1
      
      





この方法と同様に、ピクセル1から3、1から7を混合するために同じ操作を行うことができます。しかし、これらの点で、この方法は完全に無効です。 したがって、まず乗算の余地を作る必要があります。 mビットの符号なし数値にnビットを掛けると、m + nビット以下を占める数値が得られることに注意してください。 つまり、各色の前に5ビットを解放する必要があります。そのためには、ピクセルの色を偶数と奇数に分けて、それらを既に操作することができます。 そこで、ピクセルごとに2つの乗算を使用して、次のブレンド手順に進みました。

 #define Shift(p) ((p)>>5) #define OddCol(p) ((p) & 0x07E0F81F) #define EvenCol(p) ((p) & 0xF81F07E0) //      2  register unsigned int p1 = *dst; register unsigned int p2 = *(src++); //  4 ,    2   *(dst++) = OddCol( Shift(OddCol(p1)*a + OddCol(p2)*(32 - a)) ) | EvenCol( Shift(EvenCol(p1))*a + Shift(EvenCol(p2))*(32 - a) );
      
      





もう1つの乗算を取り除く方法は? 括弧を開いて用語を並べ替えてみましょう。

 C = (C1*α + C2*(32 - α)) >> 5 = (C1 - C2)*α >> 5 + C2
      
      





ただし、括弧内に負の数が表示される場合があり、負の数の算術演算はオーバーフローに基づいています。 したがって、この形式の式を計算の並列化に適用することはできません。 したがって、可能な最大値を追加して負の値を取り除きます。

 C = (C1 - C2 + 32 - 32)*α >> 5 + C2 = (C1 - C2 + 32)*α >> 5 + C2 - α
      
      





この式と偶数色と奇数色のトリックを使用すると、ピクセルごとに1つの乗算のみを使用するピクセル混合手順を取得できます。

 #define OddCol(p) ((p) & 0x07E0F81F) #define EvenCol(p) ((p) & 0xF81F07E0) register unsigned int p1 = *dst; register unsigned int p2 = *(src++); register unsigned int oddA = (a<<22) | (a<<11) | (a); register unsigned int evenA = (a<<27) | (a<<16) | (a<<6); register unsigned int oddP2 = OddCol(p2); register unsigned int evenP2 = EvenCol(p2); // 2    2  oddCol = (((OddCol(p1) - oddP2 + 0x08010020) * a) >> 5) + oddP2 - oddA; evenCol = ((( (EvenCol(p1)>>5) - (evenP2>>5) + 0x08010040) * a) + evenP2 - evenA ); *(dst++) = OddCol(oddCol) | EvenCol(evenCol);
      
      






All Articles