2012-02-16 62 views
4

可以重寫下面的函數來使用任何優化的機制?我很確定,這不是繼續進行的方式,逐個像素地進行復制。如何將位圖像素複製到其他位圖保留在C#中的透明度?

我已閱讀約AlphaBlendBitBlt,但我不習慣本地代碼。

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),所以我需要調整alpha通道。這是唯一適合我的機制。

爲什麼SRC形象有一個錯誤的像素格式說明here的原因。

我嘗試以下選項沒有運氣:

  • 創建一個新的圖像,然後使用Graphics.DrawImage從SRC的SRC圖像。沒有保留阿爾法。
  • 使用Scan0表格src創建新圖像。工作正常,但GC處理src圖像時出現問題(在其他post中解釋);

該解決方案是唯一真正有效的解決方案,但我知道這並不是最佳解決方案。我需要知道如何使用WinAPI或其他最佳機制來做到這一點。

非常感謝!

+1

應該如何優化?是否使用不安全的塊和指針算術可行的選項?我有軼事證據顯示比較雙維數組和指針時速度提高了3倍。 – 2012-02-16 21:42:06

回答

6

假設源圖像實際上每像素有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返回位圖最後一行的第一個像素。

因此,我修改了代碼以一次複製一行。

注意:我只修改了安全代碼。不安全的代碼仍然會對這兩個圖像採取積極的步伐!

+0

感謝您的回答,但不可能在我的公司(公司規則)中編譯不安全,很抱歉。你能否使用編組提供解決方案? – 2012-02-17 09:22:01

+0

@DanielPeñalba當然,更新了我的答案。 – Rotem 2012-02-17 10:05:22

+0

嘗試從srcScan0複製到緩衝區時出現以下錯誤:「嘗試讀取或寫入受保護的內存,這通常表示其他內存已損壞」 – 2012-02-17 11:30:21

0
+0

源圖像的像素格式錯誤(Format32bppRgb),所以我需要調整Alpha通道。這是唯一適用於我的機制,我更新了我的問題。 – 2012-02-16 21:23:04

+0

請查看我的所有edtis,並在可能的情況下刪除您的答案。 – 2012-02-16 21:26:36

+1

@DanielPeñalba現在我會在這裏留下我的答案,因爲如果我沒有其他人會拿出相同的答案。 – 2012-02-16 21:40:52

0

我的Codeblocks庫http://codeblocks.codeplex.com中的實用工具類允許您使用LINQ將源圖像轉換爲任何其他圖像。

看到這個樣品在這裏:http://codeblocks.codeplex.com/wikipage?title=Linq%20Image%20Processing%20sample&referringTitle=Home

雖然樣品轉換源和目的地之間的相同的圖像格式,你可以改變周圍的事物,也是如此。

請注意,我已經爲此代碼計時,它比大型圖像的不安全代碼快得多,因爲它使用緩存的全行讀取。

+0

我正在使用.NET 2,對不起。不管怎麼說,還是要謝謝你。 – 2012-02-17 09:08:22

+0

哦,好吧。我會在這裏留下這個答案,以防其他使用.NET 3.5的人發現它! :) – Ani 2012-02-17 14:50:47

相關問題