2014-01-23 3 views
1

여기에서 읽은 것처럼 memcpy를 사용하여 24bpp 이미지를 자르려고합니다 : cropping an area from BitmapData with C#. 문제는 내 sourceImage가 32bpp 일 때만 작동한다는 것입니다. 내 sourceImage가 24bpp 일 때 이미지가 손상됩니다. 내가 변화하고있어 유일한 것은이 sourceImage PixelFormat이 아닌 CropBitmap 방법의 코드 중 하나이기 때문에memcpy를 사용한 이미지 자르기가 24bpp 이미지에서 실패합니다.

class Program 
{ 
    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
    static unsafe extern int memcpy(byte* dest, byte* src, long count); 

    static void Main(string[] args) 
    { 
     var image = new Bitmap(@"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\adsadas.png"); 

     //Creates a 32bpp image - Will work eventhough I treat it as a 24bpp image in the CropBitmap method... 
     //Bitmap newBitmap = new Bitmap(image); 

     //Creates a 24bpp image - Will produce a corrupt cropped bitmap 
     Bitmap newBitmap = (Bitmap)image.Clone(); 

     var croppedBitmap = CropBitmap(newBitmap, new Rectangle(0, 0, 150, 150)); 
     croppedBitmap.Save(@"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\PieceOfShit.png", ImageFormat.Png); 

     Console.ReadLine(); 
    } 

    static public Bitmap CropBitmap(Bitmap sourceImage, Rectangle rectangle) 
    { 
     Console.WriteLine("Bits per pixel of sourceImage: {0}", Image.GetPixelFormatSize(sourceImage.PixelFormat)); 

     var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

     var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format24bppRgb); 
     var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); 

     unsafe 
     { 
      byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer(); 
      byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer(); 
      memcpy(croppedImagePointer, sourceImagePointer, croppedBitmapData.Stride * rectangle.Height); 
     } 
     sourceImage.UnlockBits(sourceBitmapdata); 
     croppedImage.UnlockBits(croppedBitmapData); 
     return croppedImage; 
    } 
} 

나는 매우 혼란 스러워요. 그래서 나는 항상 32bpp sourceImage 경우에도 24bpp Pixelformat 사용하여 LockBits 호출합니다.

복사하는 바이트 수를 계산하는 여러 가지 방법을 시도했지만 결과적으로 모두 동일한 손상된 이미지가 나타납니다.

도움을 주시면 감사하겠습니다.

+1

당신은 코드를 디버깅하고 값을 확인 했습니까? 'Stride' 값은 음수 일 수 있습니다. 비트 맵은 거꾸로 메모리에 저장됩니다. – Guffa

+0

32bpp sourceImage 또는 24bpp sourceImage를 사용할 경우 보폭이 동일합니다. (이것은 비트를 잠그면 32bpp sourceImage를 24bpp 이미지로 처리하기 때문일 것입니다.) – VincentC

+0

'memcpy'를 사용할 필요가 없으며 대신'Marshal' 클래스를 사용하십시오. 더 안전합니다. – Dai

답변

1

데이터를 하나의 연속 블록처럼 복사하려하지만 그렇지 않습니다.

이미지 데이터는 스캔 라인에 정렬되지만 이미지의 일부를 선택하면 각 스캔 라인의 모든 데이터를 원하지 않으므로 선택한 픽셀을 나타내는 데이터 만 필요합니다. . 스캔 라인에는 LockBits을 호출 할 때 지정한 픽셀의 데이터뿐만 아니라 해당 영역 외부의 픽셀에 대한 데이터도 포함됩니다.

Stride 값은 한 스캔 라인에서 다음 스캔 라인까지의 메모리 주소의 차이입니다. Stride 값은 스캔 라인 사이의 패딩을 포함 할 수도 있습니다. Stride 값은 음수 일 수 있으며 이미지 데이터가 메모리에 거꾸로 저장된 경우 발생합니다.

소스 이미지의 한 줄에서 대상 이미지의 줄로 관련 데이터를 복사하려고합니다. 원본 데이터와 대상 데이터에 모두 간격이있을 수 있으므로 데이터를 단일 데이터 묶음으로 복사 할 수 없습니다.

당신은 라인을 통해 루프를 필요로하고, 내가 테스트하지 않았습니다 별도로 코드를 각 라인을 복사하지만,이 같은 뭔가 것 :

byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer(); 
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer(); 
int width = rectange.Width * 3; // for 24 bpp pixel data 
for (int y = 0; y < rectangle.Height; y++) { 
    memcpy(croppedImagePointer, sourceImagePointer, width); 
    sourceImagePointer += sourceBitmapdata.Stride; 
    croppedImagePointer += croppedBitmapData.Stride; 
} 
+0

자세한 설명을 부탁드립니다! 나는 코드를 테스트했고 완벽하게 작동합니다! – VincentC

관련 문제