2011-07-07 8 views
1

私は.NET 4.0を使用しています。私はこれがフレームワークのバグかGDI +のものかどうかはわかりません。私はちょうど色チャンネルを交換するためのアプリを書いている間にそれを発見した。アルファ値が0の色の値が黒く表示されます

問題を説明してみましょう。あるビットマップからピクセルを読み込み、チャンネルを入れ替えて別のビットマップに書き出しています。 (具体的には、出力画像のRGB値を入力画像のアルファに、出力アルファを入力のグリーンチャネルに等しく設定しています。または、A => RGBとG => Aです。次のように:

for (int y = 0; y < input.Height; y++) 
{ 
    for (int x = 0; x < input.Width; x++) 
    { 
     Color srcPixel = input.GetPixel(x, y); 

     int alpha = srcPixel.A; 
     int green = srcPixel.G; 

     Color destPixel = Color.FromArgb(green, alpha, alpha, alpha); 

     output.SetPixel(x, y, destPixel); 
    } 
} 

を同様に、私はこの試みた:ほとんどの部分について

 int color = green << 24 | alpha << 16 | alpha << 8 | alpha; 

     Color destPixel = Color.FromArgb(color); 

     output.SetPixel(x, y, destPixel); 

、それが動作します。

問題点:RGBの値に関係なく、アルファがゼロの場合、結果のRGB値は常に純粋な黒(R:0、G:0、B:0)です。私はこれが何らかのFromArgb()最適化であるかどうかわかりません。.NET Reflectorを使用して、FromArgb()が何か変なことをしていないか、Bitmap.SetPixelが原因です。ネイティブコードと私はそれを見ることはできません。どちらの方法でも、アルファがゼロの場合、ピクセルは黒です。これはではない私が期待した動作です。私はRGBチャンネルをそのまま維持する必要があります。

最初は私がそれを事前に乗算されたアルファの問題だと思っていました。なぜなら、自家製のDDSローダー(私は仕様に基づいて作成したもので、何も問題を与えていない)を使ってDDSファイルをロードしているからです。このように、255の明示的なアルファを指定します - それらのどれもが、黒が判明していない、すなわち -

 Color destPixel = Color.FromArgb(255, alpha, alpha, alpha); 

... RGBチャンネルが正しく表示されますので、間違いなく誤ってRGB値を安全にすることができ前提としてGDI +内の何かアルファがゼロの場合は無視されます...私にとっては、かなり愚かな仮定のように見えますが、何でも。

さらに問題を悪化させるのは、色の種類が不変で構造体には意味がありますが、色を作成してアルファを割り当てることができないということです。SetPixel()が原因であれば、とにかく問題はない。 (私はこれを設定した直後に再びピクセルを取得し、同じ結果を見てテストしました。ゼロアルファ=ゼロRGBです)。

私の質問:誰もこの問題に対処して、比較的簡単な回避策を思い付いていますか?私の依存関係を抑えるために、サードパーティのイメージライブラリをインポートするのは嫌ですが、GDI +は自分のカラーチャネルに関するバグの前提をしているので、選択肢がないかもしれません。

ありがとうございました。

編集:私はこれを解決しましたが、さらに7時間は回答を投稿できません。驚くばかり。

答えて

1

申し訳ありません。とにかく、私は5〜10分後に解決策を見つけたので、投稿する前にこれをもう少し長く作業していたはずです。明確にするために、私はGDI +の問題について解決策を見つけることはできませんでしたが、適切な回避策が見つかりました。他のAPIでは、サーフェスをロックしてバイトを別のサーフェスに直接転送する方法について考えました。そのアプローチをとったのです。 MSDNから少し助けた後、ここに私のコードは、(サンセリフのエラー処理)です:

Bitmap input = Bitmap.FromFile(filename) as Bitmap; 

int byteCount = input.Width * input.Height * 4; 

var inBytes = new byte[byteCount]; 
var outBytes = new byte[byteCount]; 

var inBmpData = input.LockBits(new Rectangle(0, 0, input.Width, input.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 

Marshal.Copy(inBmpData.Scan0, inBytes, 0, byteCount); 

for (int y = 0; y < input.Height; y++) 
{ 
    for (int x = 0; x < input.Width; x++) 
    { 
     int offset = (input.Width * y + x) * 4; 

    // byte blue = inBytes[offset]; 
     byte green = inBytes[offset + 1]; 
    // byte red = inBytes[offset + 2]; 
     byte alpha = inBytes[offset + 3]; 

     outBytes[offset]  = alpha; 
     outBytes[offset + 1] = alpha; 
     outBytes[offset + 2] = alpha; 
     outBytes[offset + 3] = green; 
    } 
} 

input.UnlockBits(inBmpData); 

Bitmap output = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb); 

var outBmpData = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, output.PixelFormat); 

Marshal.Copy(outBytes, 0, outBmpData.Scan0, outBytes.Length); 

output.UnlockBits(outBmpData); 

注:元帥はSystem.Runtime.InteropServices下にあります。 BitmapData(inBmpData、outBmpData)、ImageLockMode、PixelFormatはSystem.Drawing.Imagingの下にあります。

これは完全に機能するだけでなく、驚くほど高速です。私はこの技術を今から私のすべてのチャンネルスワップのニーズに使うつもりです。 (私は既に別の類似のアプリで使っています)

申し訳ありません。私は少なくともこのソリューションが他の人に役立つことを願っています。

関連する問題