我正在尝试使用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。
我尝试过不同的方法来计算我正在复制的字节数,但是所有方法都会导致或多或少相同的图像损坏。
感谢任何帮助!
答案 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;
}