2012-10-31 67 views
2

我在將Bitmap.GetPixel中的某些代碼更改爲使用LockBits返回的直接像素緩衝區時遇到了問題。看起來LockBits返回的數據確實給了我與GetPixel相比不同的Color值。Bitmap.LockBits給出錯誤的值?

,由於該變化不會產生這會破自動化的單元測試不同的顏色是不幸的。我有一個29 * 30像素的png文件,我將Format32bppArgb加載到位圖中。這真的可能是由LockBits和GetPixel引發的數據不同。我怎樣才能解決這個問題?

下面是一些代碼加載位圖

public unsafe static Bitmap Convert(Bitmap originalBitmap) 
{ 
    Bitmap converted = new Bitmap(originalBitmap); 
    Rectangle rect = new Rectangle(0, 0, converted.Width, converted.Height); 
    var locked = converted.LockBits(rect, ImageLockMode.ReadWrite, originalBitmap.PixelFormat); 
    byte* pData = (byte*)locked.Scan0; 

    // bytes per pixel 
    var bpp = ((int)converted.PixelFormat >> 11) & 31; 

    byte* r; 
    byte* g; 
    byte* b; 
    byte* a; 

    for (int y = 0; y < locked.Height; y++) 
    { 
     var row = pData + (y * locked.Stride); 

     for (int x = 0; x < locked.Width; x++) 
     { 
      a = row + x * bpp + 3; 
      r = row + x * bpp + 2; 
      g = row + x * bpp + 1; 
      b = row + x * bpp + 0; 
      var col = Color.FromArgb(*a, *r, *g, *b); 
      var origCol = originalBitmap.GetPixel(x, y); 
      if (origCol != col) 
      { 
       Debug.Print("Orig: {0} Pixel {1}", origCol, col); 
      } 

     } 
    } 
    converted.UnlockBits(locked); 

    return converted; 
} 

Orig: Color [A=128, R=128, G=128, B=255] Pixel Color [A=128, R=127, G=127, B=255] 
Orig: Color [A=0, R=128, G=128, B=255] Pixel Color [A=0, R=0, G=0, B=0] 

Orig: Color [A=45, R=128, G=128, B=255] Pixel Color [A=45, R=130, G=130, B=254] 
                 ok  -2  -2  +1 

大部分的時間攝製,但似乎有一些四捨五入和轉換將繼續。我可以強制LockBits返回GetPixel返回的數據嗎?

+0

不能PNG格式包含顏色配置文件/伽馬設置/什麼?也許'GetPixel'會根據顏色配置文件檢索像素的顏色。很顯然,原始ARGB值不能存儲顏色配置文件,因此您會看到不同的結果。 –

+0

您的bpp變量是處理像素格式的嘗試。但除非它是32bpp圖像,否則實際上不能工作。至少要求在LockBits()調用中需要的像素格式。 PArgb將是一個問題。它也可以在原始圖像上使用LockBits()。但是,再也沒有任何意義了。 –

+0

我在我的測試數據中找到了一些24/32bpp圖像。是的,我拒絕了其他一切。 bpp變量提取代碼事實上根本不起作用,因爲它報告錯誤的字節數。現在我有一個支持格式的開關盒。 –

回答

5

據我所知,PNG格式可以包含顏色配置文件和伽瑪修正信息以及其他任何能夠影響的像素與他們的原始表示的最終顏色。

即使不理會有關PNG格式的具體知識,一般GetPixel可以比預計返回不同的值。

Bitmap.GetPixel,從而實現:

public Color GetPixel(int x, int y) 
{ 

    int color = 0; 

    if (x < 0 || x >= Width) 
    { 
     throw new ArgumentOutOfRangeException("x", SR.GetString(SR.ValidRangeX)); 
    } 

    if (y < 0 || y >= Height) 
    { 
     throw new ArgumentOutOfRangeException("y", SR.GetString(SR.ValidRangeY)); 
    } 

    int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel(new HandleRef(this, nativeImage), x, y, out color); 

    if (status != SafeNativeMethods.Gdip.Ok) 
     throw SafeNativeMethods.Gdip.StatusException(status); 

    return Color.FromArgb(color); 
} 

SafeNativeMethods.Gdip.GdipBitmapGetPixel的定義是:

[DllImport(ExternDll.Gdiplus, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Unicode)] // 3 = Unicode 
[ResourceExposure(ResourceScope.None)] 
internal static extern int GdipBitmapGetPixel(HandleRef bitmap, int x, int y, out int argb); 

這一點我們從here學習是Gdiplus::Bitmap::GetPixel。該功能的文件說:

備註

取決於位圖的格式,是由Bitmap::SetPixel設置Bitmap::GetPixel可能不會返回相同的值。例如,如果在像素格式爲32bppPARGB的位圖對象上調用Bitmap::SetPixel,則會將像素的RGB分量預乘。隨後調用Bitmap::GetPixel可能因四捨五入而返回不同的值。另外,如果在顏色深度爲每像素16位的Bitmap對象上調用Bitmap::SetPixel,則在從32位轉換爲16位期間信息可能會丟失,並且隨後調用Bitmap::GetPixel可能會返回不同的值。

+0

好吧似乎沒有辦法解決這個問題。我的圖片格式不是PARGB,但似乎也有類似的情況發生在PNG上。 –

+0

@AloisKraus PARGB就是一個例子。 –