使用memcpy裁剪图像在24bpp图像上失败

时间:2014-01-23 21:51:57

标签: c# .net image-processing memcpy

我正在尝试使用memcpy裁剪24bpp图像,就像我在这里阅读的那样:cropping an area from BitmapData with C#。我遇到的问题是它只在我的sourceImage为32bpp时才有效。当我的sourceImage为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;
    }
}

我很困惑,因为我唯一要改变的是sourceImage PixelFormat,而不是CropBitmap方法中的任何代码。所以我总是使用24bpp Pixelformat调用LockBits,即使sourceImage是32bpp。

我尝试过不同的方法来计算我正在复制的字节数,但是所有方法都会导致或多或少相同的图像损坏。

感谢任何帮助!

1 个答案:

答案 0 :(得分: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;
}