2012-02-16 10 views
4

のメカニズムを使用するには、次の関数を書き直すことができますか?私はこれが進行する方法ではないことを確かめています。ピクセル単位でコピーしています。C#でアルファ透明度を保持するビットマップピクセルを他のビットマップにコピーする方法は?

私はAlphaBlendまたはBitBltについて読んでいますが、ネイティブコードには慣れていません。

public static Bitmap GetAlphaBitmap(Bitmap srcBitmap) 
{ 
    Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb); 

    Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); 

    BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat); 

    try 
    { 
     for (int y = 0; y <= srcData.Height - 1; y++) 
     { 
      for (int x = 0; x <= srcData.Width - 1; x++) 
      { 
       Color pixelColor = Color.FromArgb(
        Marshal.ReadInt32(srcData.Scan0, (srcData.Stride * y) + (4 * x))); 

       result.SetPixel(x, y, pixelColor); 
      } 
     } 
    } 
    finally 
    { 
     srcBitmap.UnlockBits(srcData); 
    } 

    return result; 
} 

重要注:ソース画像は、間違ったピクセル形式(Format32bppRgb)を有しているので、私は、アルファチャンネルを調整する必要があります。これは私のために働く唯一のメカニズムです。

srcイメージのピクセルフォーマットが間違っている理由は、hereです。

は、私は運なしで、次のオプションを試してみました:

  • がSRCからGraphics.drawImageをを使用してのsrcイメージを新しいイメージを作成して描画します。アルファを保存しませんでした。
  • Scan0フォームsrcを使用して新しい画像を作成する。うまく動作しますが、GCがsrcイメージを処理するときに問題があります(他の例ではpostで説明しています)。

このソリューションは実際には機能する唯一のソリューションですが、最適ではありません。私はWinAPIや他の最適なメカニズムを使ってそれを行う方法を知る必要があります。

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

+1

どのように最適化する必要がありますか?安全でないブロックとポインタを使用して実行可能なオプションを使用していますか?私は、二次元配列とポインタを比較すると速度が3倍向上したという逸話的な証拠があります。 –

答えて

6

ソースイメージが1ピクセルあたり32ビットであると仮定すると、これは安全でないコードとポインタを使用して十分速く実装する必要があります。マーシャリングを使用しても同じことが達成できますが、正しく覚えていれば約10〜20%のパフォーマンス低下が見られます。

ネイティブメソッドを使用する方が高速ですが、これはすでにSetPixelよりもはるかに速いはずです。ここで

public unsafe static Bitmap Clone32BPPBitmap(Bitmap srcBitmap) 
{ 
    Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb); 

    Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); 
    BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat); 
    BitmapData resData = result.LockBits(bmpBounds, ImageLockMode.WriteOnly, result.PixelFormat); 

    int* srcScan0 = (int*)srcData.Scan0; 
    int* resScan0 = (int*)resData.Scan0; 
    int numPixels = srcData.Stride/4 * srcData.Height; 
    try 
    { 
     for (int p = 0; p < numPixels; p++) 
     { 
      resScan0[p] = srcScan0[p]; 
     } 
    } 
    finally 
    { 
     srcBitmap.UnlockBits(srcData); 
     result.UnlockBits(resData); 
    } 

    return result; 
} 

は、マーシャリングを使用して、この方法の安全なバージョンです:

public static Bitmap Copy32BPPBitmapSafe(Bitmap srcBitmap) 
{ 
    Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb); 

    Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); 
    BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat); 
    BitmapData resData = result.LockBits(bmpBounds, ImageLockMode.WriteOnly, result.PixelFormat); 

    Int64 srcScan0 = srcData.Scan0.ToInt64(); 
    Int64 resScan0 = resData.Scan0.ToInt64(); 
    int srcStride = srcData.Stride; 
    int resStride = resData.Stride; 
    int rowLength = Math.Abs(srcData.Stride); 
    try 
    { 
     byte[] buffer = new byte[rowLength]; 
     for (int y = 0; y < srcData.Height; y++) 
     { 
      Marshal.Copy(new IntPtr(srcScan0 + y * srcStride), buffer, 0, rowLength); 
      Marshal.Copy(buffer, 0, new IntPtr(resScan0 + y * resStride), rowLength); 
     } 
    } 
    finally 
    { 
     srcBitmap.UnlockBits(srcData); 
     result.UnlockBits(resData); 
    } 

    return result; 
} 

編集:あなたの元画像のみ(走査線がメモリ内に逆さまに保存されている意味し、負のストライドを持っていますy軸上では、行はまだ左から右に移動します)。これは実質的には.Scan0がビットマップの最後の行の最初のピクセルを返すことを意味します。

私は一度に1行をコピーするようにコードを修正しました。

注意:私は安全コードのみを変更しました。安全でないコードでは、両方の画像に対して積極的な進歩が見られます。

+0

あなたの答えをありがとうが、私の会社(会社の規則)で安全でないものをコンパイルすることは不可能です、ごめんなさい。マーシャリングを使用してソリューションを提供できますか? –

+0

@DanielPeñalba確かに、私の答えを更新しました。 – Rotem

+0

srcScan0からバッファにコピーしようとすると、「保護されたメモリを読み書きしようとしましたが、他のメモリが壊れていることがよくあります」 –

0

Bitmap Clone methodを試してください。

+0

ソースイメージのピクセル形式が正しくないため(Format32bppRgb)、アルファチャンネルを調整する必要があります。これは私のために働く唯一のメカニズムです、私は私の質問を更新しました。 –

+0

私のすべてのedtisを見て、可能であればあなたの答えを削除してください。 –

+1

@DanielPeñalba私はここで私の答えを残しておきます。なぜなら私が誰かが同じ答えを考え出すことがないからです。 –

0

私のCodeblocksライブラリのユーティリティクラスhttp://codeblocks.codeplex.comを使用すると、ソースイメージをLINQを使用して他のイメージに変換できます。

はここに、このサンプルを参照してください:http://codeblocks.codeplex.com/wikipage?title=Linq%20Image%20Processing%20sample&referringTitle=Home

サンプルは、送信元と宛先の間で同じ画像形式を変換しながら、あなたにも、周りのものを変更することができます。

私はこのコードを実行しており、大量の画像では安全でないコードよりもはるかに高速です。これは、キャッシュされたフル・ローの先読みを使用するためです。

+0

私は.NET 2を使用しています。とにかくありがとう。 –

+0

ああ、よく。 .NET 3.5を使用している他の人が見つけた場合に備えて、この回答をここに残しておきます! :) – Ani

関連する問題