2012-10-31 4 views
2

LockBits에서 반환 한 직접 픽셀 버퍼를 사용하여 Bitmap.GetPixel에서 일부 코드를 변경하는 동안 문제가 발생했습니다. LockBits에 의해 반환 된 데이터가 GetPixel에 비해 다른 색상 값을 부여하는 것 같습니다.Bitmap.LockBits가 잘못된 값을 제공합니까?

이 변경으로 인해 자동화 된 단위 테스트가 중단되는 다른 색상이 만들어지기 때문에 불행합니다. 나는 Format32bppArgb를 비트 맵에로드하는 29 * 30 픽셀의 png 파일을 가지고 있습니다. LockBits와 GetPixel에 의한 데이터 returend가 다른 것일 수 있습니다. 이 문제를 어떻게 해결할 수 있습니까? 여기

로드 된 비트 맵 대부분의 시간

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 

그것을 repro 수 몇 가지 코드 만에 갈 몇 가지 반올림 및 변환있을 것 같습니다. 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의 정의는 다음과 같습니다 우리가 here 배울

[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); 

Gdiplus::Bitmap::GetPixel입니다. 그 기능에 대한 문서는 말한다 :

는 비트 맵의 ​​형식에 따라

비고 Bitmap::SetPixel가되면서 Bitmap::GetPixel가 같은 값을 반환하지 않을 수 있습니다. 예를 들어 픽셀 형식이 32bppPARGB 인 Bitmap 객체에서 Bitmap::SetPixel을 호출하면 픽셀의 RGB 구성 요소에 미리 곱해집니다. 이후의 Bitmap::GetPixel에 대한 호출은 반올림으로 인해 다른 값을 반환 할 수 있습니다. 또한 색상 심도가 픽셀 당 16 비트 인 Bitmap 객체에서 Bitmap::SetPixel을 호출하면 32 비트에서 16 비트로 변환하는 동안 정보가 손실 될 수 있으며 이후에 Bitmap::GetPixel을 호출하면 다른 값이 반환 될 수 있습니다.

+0

좋아,이 문제를 해결할 방법이없는 것 같습니다. 내 그림 형식은 PARGB가 아니지만 PNG와 비슷한 점이 있습니다. –

+0

@AloisKraus PARGB는 그 예입니다. –

관련 문제