2016-05-30 27 views
0

外部ネイティブライブラリ(C++)から、16Bit RGB(SlimDX相当のものはB5G6R5_UNorm)のように見えるピクセルバッファがあります。ピクセルバッファをB8G8R8A8_UNormから16Bitに変換する

Direct2Dを使用してこのバッファで表されるイメージを表示します。しかし、Direct2DはB5G6R5_UNormをサポートしていません。

私はB8G8R8A8_UNorm

この画素バッファを変換する必要がある私は、ビットシフトの方法を用いて、そのようなタスクの種々のコードスニペットを見ているが、のいずれも自分のニーズやフォーマットに特異的でありませんでした。それは私が0、nada、none、zilchビットシフトについての手掛かり、またはそれがどのように行われているかを助けるものではありません。

何私は後にしていますが、変換を行うための方法で構築されたようなタスク、または任意のC♯のコード例である - に注意してください、私は他のライブラリの

を使用して気にしない:私はこれを使用して行うことができます知っています私は好きではないGDIについて何かがあります)、画像(ピクセルバッファの形で)が厚くて速く来るでしょう、そして、私はSlimDXを使いやすさとパフォーマンスを考慮して選択しています。

私はピクセルバッファを変換する必要があると考えている理由は、B8G8R8A8_UNormを使用して画像を描画すると、画像が緑色に覆われており、ピクセルはちょうどその場所全体にあるため、ピクセルバッファを必要なフォーマットに「アップグレード」する。

ちょうど追加:バッファを変換せずに上記を行うと、イメージはジオメトリ全体を満たしません。画像フォーマットを扱うとき

ピクセルバッファがbyte[]オブジェクト

答えて

1

ビットシフトと論理演算子を介して提供されているが、本当に便利ですので、あなたはそれについての詳細を読むために自分自身にそれを借りています。しかし、私はあなたに、このピクセルフォーマットが表すものと、変換する方法を素早く実行することができます。私は本当にC#とそのサポートライブラリをよく知っていないので、あなたのためのインボックスソリューションがあるかもしれないという警告で私の答えを序文にする必要があります。

まず、ピクセルバッファのフォーマットはB5G6R5_UNORMです。したがって、各ピクセルに16ビット(赤5、緑6、青5)が割り当てられています。このピクセルフォーマットのビットレイアウトを「RRRRRGGGGGBBBBB」と視覚化することができます。ここで、「R」は赤のチャネルに属するビットを表し、「G」は緑のチャネルに属するビットを表し、「B」は青いチャンネルあなたのピクセルフォーマットのビットレイアウトと今

、のは、あなたのピクセルバッファの最初の16ビット(2バイト)を言わせている1111110100101111.ラインは、そのアップ...

RRRRRGGGGGGBBBBB 
1111110100101111 

これは赤のチャンネルを意味していますビット11111、緑は101001、青は01111です。バイナリから小数への変換:赤= 31、緑= 41、青= 15。赤のチャンネルはすべてのビットが1に設定されていますが、その値(31)は実際に緑のチャンネル(41)よりも小さいことがわかります。しかし、これは、表示されたときの色が赤よりも緑色であることを意味するものではありません。緑のチャンネルに余分なビットがあるので、赤と青のチャンネルよりも多くの値を表すことができますが、この特定の例では実際に出力色に赤がより多く表示されます。それはUNORMの部分が入っている場所です...

UNORMは符号なし正規化整数を表します。これは、カラーチャネル値が0.0から1.0までの等間隔の浮動小数点数として解釈されることを意味します。値は、割り当てられたビット数で正規化されます。正確にはどういう意味ですか?チャンネルを保存するためのフォーマットが3ビットしかないとします。つまり、チャンネルは2^3 = 8の異なる値を持つことができ、それぞれの小数点、2進数、正規化された表現で下に示されています。正規化された値は、Nビットで表すことができる最大の可能な10進値で除算した単なる小数値です。

Decimal | Binary | Normalized 
----------------------------- 
0  | 000 | 0/7 = 0.000 
1  | 001 | 1/7 =~ 0.142 
2  | 010 | 2/7 =~ 0.285 
3  | 011 | 3/7 =~ 0.428 
4  | 100 | 4/7 =~ 0.571 
5  | 101 | 5/7 =~ 0.714 
6  | 110 | 6/7 =~ 0.857 
7  | 111 | 7/7 = 1.000 

バックピクセルがビット1111110100101111を持っていた以前の例に行く、我々は既に3つのカラーチャンネルのための私達の十進値を知っている:RGB = {31、41、15}。小数点の値は誤解を招くため、正規化された値が必要です。格納されたビットの数を知らなければ、あまり知らせません。赤と青のチャネルは5ビットで格納されるため、小数点の最大値は2^5 -1 = 31;ただし、緑のチャネルの最大小数値は2^6-1 = 63です。これを知って、正規化されたカラーチャネルである:彼らは出力で各色チャンネルの相対的な寄与を表すため、繰り返しに

// NormalizedValue = DecimalValue/MaxDecimalValue 
R = 31/31 = 1.000 
G = 41/63 =~ 0.650 
B = 15/31 =~ 0.483 

、正規化された値が有用です。特定のチャンネルにビットを追加しても、使用可能な色の範囲には影響しません。色の精度が向上します(基本的に色の濃淡が増えます)。

上記をすべて知っていれば、各チャンネルに格納されているビット数に関係なく、RGB(A)形式からRGB(A)形式に変換することができます。たとえば、正規化したばかりの値をB8G8R8A8_UNORMに変換してみましょう。計算した値を正規化した後は、新しい形式の最大値だけでスケーリングするので、これは簡単です。すべてのチャネルは8ビットを使用するため、最大値は2^8-1 = 255です。元のフォーマットにはアルファチャンネルがないので、通常は最大値(完全に不透明であることを意味します)を保存するだけです。

// OutputValue = InputValueNormalized * MaxOutputValue 
B = 0.483 * 255 = 123.165 
G = 0.650 * 255 = 165.75 
R = 1.000 * 255 = 255 
A = 1.000 * 255 = 255 

これをコード化する前に、紛失していることは1つだけです。上記のように、各チャンネルのビットを一列に並べてコピーするだけで、ビットを引き出すことができました。それで私は緑色のビット101001を得ました。コードでは、これは気にしないビットを「マスク」することによって行うことができます。シフトはちょうどそのように聞こえる:ビットを右または左に動かす。ビットを右に移動すると、右端のビットが破棄され、新しい左端のビットに0が割り当てられます。上記の16ビットの例を使用して以下の視覚化。

あなたはシフトし続けることができ、最終的には16の0で終わるでしょう。しかし、私は理由のために5つのシフトで止まった。 6右端のビットは緑のビットです(私は5つの青のビットをシフト/破棄しました)。私たちは必要な正確なビットをほとんど抽出しましたが、緑色のビットの左側に余分な5ビットが残っています。これらを削除するには、「論理AND」演算を使用して、右端の6ビットのみをマスクします。バイナリ形式のマスクは0000000000111111です。 1は我々がビットを望むことを意味し、0は我々がそれを望まないことを意味する。マスクは、最後の6つの位置を除いてすべて0である。なぜなら、最後の6ビットしか必要ないからである。これは5倍にアップマスクライン数をシフトし、両方のビットが1であり、他のすべてのビットのための0時に出力が1:

0000011111101001 // original 16 bits shifted 5x to the right 
0000000000111111 // bit mask to extract the rightmost 6 bits 
------------------------------------------------------------ 
0000000000101001 // result of the 'logical and' of the two above numbers 

結果は、我々が探しているまさに数:6であるグリーンビットと他に何もありません。先頭の0は小数点の値に影響しないことを思い出してください(まだ41です)。 C#や他のC言語のような 'shift right'(>>)や 'logical and'(&)操作は非常に簡単です。これはC#のように見えます:

// 0xFD2F is 1111110100101111 in binary 
uint pixel = 0xFD2F; 

// 0x1F is 00011111 in binary (5 rightmost bits are 1) 
uint mask5bits = 0x1F; 

// 0x3F is 00111111 in binary (6 rightmost bits are 1) 
uint mask6bits = 0x3F; 

// shift right 11x (discard 5 blue + 6 green bits), then mask 5 bits 
uint red = (pixel >> 11) & mask5bits; 

// shift right 5x (discard 5 blue bits), then mask 6 bits 
uint green = (pixel >> 5) & mask6bits; 

// mask 5 rightmost bits 
uint blue = pixel & mask5bits; 

このように見えるのは、このようなルーティンです。しかし、エンディアンを読んで、期待どおりのバイト順序になっていることを確認してください。この場合、パラメータは32ビットの符号なし整数(最初の16ビットは無視されます)

byte[] R5G6B5toR8G8B8A8(UInt16 input) 
{ 
    return new byte[] 
    { 
     (byte)((input & 0x1F)/31.0f * 255),   // blue 
     (byte)(((input >> 5) & 0x3F)/63.0f * 255), // green 
     (byte)(((input >> 11) & 0x1F)/31.0f * 255), // red 
     255           // alpha 
    }; 
} 
+0

ジャスティンです。情報ありがとうございます。私はこの数週間の間、これをかなり頻繁に言及します。あなたの例と細部をうまく活用するでしょう!その詳細な、情報と答えた!ありがとうございました –

関連する問題