倾斜位图,RGB565 C#的步幅计算
Slanted bitmap, stride calculation for RGB565 C#
我生成的一些图像是倾斜的,有些不是。
预期结果:(529x22)
实际结果:(529x22)
Don't mind the different image sizes, these are screenshots. They are both 529x22.
我正在使用的代码,我刚刚从 SO 上的一个问题的答案中得到这个。
// some other method
byte[] pixels = new byte[size - 16];
Array.Copy(this.data, offset, pixels, 0, pixels.Length);
this.ByteToImage(w, h, pixels);
// builds the pixels to a image
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// bytes => not using this because it gives error
// eg. pixel.Length = 16032, bytes = 16064
int bytes = bmpData.Stride * bmp.Height;
Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
我很困惑,因为有些工作正常,没有倾斜。但其他人是倾斜的。我错过了什么?
更新
如评论和答案中所述,问题在于我如何计算步幅。我仍然对如何做感到困惑,但我试过了:
public static void RemovePadding(this Bitmap bitmap)
{
int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var pixels = new byte[bitmapData.Width * bitmapData.Height * bytesPerPixel];
for (int row = 0; row < bitmapData.Height; row++)
{
var dataBeginPointer = IntPtr.Add(bitmapData.Scan0, row * bitmapData.Stride);
Marshal.Copy(dataBeginPointer, pixels, row * bitmapData.Width * bytesPerPixel, bitmapData.Width * bytesPerPixel);
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
bitmap.UnlockBits(bitmapData);
}
但结果是(更倾斜):
这期望步幅对应于宽度,没有填充。可以有填充。填充将 "eat" 一些下一行,因此看起来向左移动。
由于填充打断了行,唯一真正的处理方法(除了在所有地方使用相同的填充之外)是逐行复制。可以用bmpData.Scan0 + y * bmpData.Stride
计算一行的起始地址。从那里开始复制,每个 y
.
// bytes => not using this because it gives error
是的,因为您的数组没有填充。所以你告诉它复制比数组保存更多的数据。
这似乎在这里工作:
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
byte bpp = 2;
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// copy line by line:
for (int y = 0; y < h; y++ )
Marshal.Copy(pixels, y * w * bpp, bmpData.Scan0 + bmpData.Stride * y, w * bpp);
bmp.UnlockBits(bmpData);
return bmp;
}
我使用循环将每一行数据放在正确的位置。数据不包括填充,但目标地址必须这样做。
因此我们需要将 数据访问 乘以实际 width * bytePerPixel
但 目标地址 乘以 Stride
,即扫描线的长度,填充到下一个四字节的倍数。对于 width=300
它是 stride=300
,对于 width=301
它是 stride=304
..
一步移动所有像素数据只有在没有填充时才有效,即当宽度是 4
的倍数时。
我生成的一些图像是倾斜的,有些不是。
预期结果:(529x22)
实际结果:(529x22)
Don't mind the different image sizes, these are screenshots. They are both 529x22.
我正在使用的代码,我刚刚从 SO 上的一个问题的答案中得到这个。
// some other method
byte[] pixels = new byte[size - 16];
Array.Copy(this.data, offset, pixels, 0, pixels.Length);
this.ByteToImage(w, h, pixels);
// builds the pixels to a image
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// bytes => not using this because it gives error
// eg. pixel.Length = 16032, bytes = 16064
int bytes = bmpData.Stride * bmp.Height;
Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
我很困惑,因为有些工作正常,没有倾斜。但其他人是倾斜的。我错过了什么?
更新
如评论和答案中所述,问题在于我如何计算步幅。我仍然对如何做感到困惑,但我试过了:
public static void RemovePadding(this Bitmap bitmap)
{
int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var pixels = new byte[bitmapData.Width * bitmapData.Height * bytesPerPixel];
for (int row = 0; row < bitmapData.Height; row++)
{
var dataBeginPointer = IntPtr.Add(bitmapData.Scan0, row * bitmapData.Stride);
Marshal.Copy(dataBeginPointer, pixels, row * bitmapData.Width * bytesPerPixel, bitmapData.Width * bytesPerPixel);
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
bitmap.UnlockBits(bitmapData);
}
但结果是(更倾斜):
这期望步幅对应于宽度,没有填充。可以有填充。填充将 "eat" 一些下一行,因此看起来向左移动。
由于填充打断了行,唯一真正的处理方法(除了在所有地方使用相同的填充之外)是逐行复制。可以用bmpData.Scan0 + y * bmpData.Stride
计算一行的起始地址。从那里开始复制,每个 y
.
// bytes => not using this because it gives error
是的,因为您的数组没有填充。所以你告诉它复制比数组保存更多的数据。
这似乎在这里工作:
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
byte bpp = 2;
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// copy line by line:
for (int y = 0; y < h; y++ )
Marshal.Copy(pixels, y * w * bpp, bmpData.Scan0 + bmpData.Stride * y, w * bpp);
bmp.UnlockBits(bmpData);
return bmp;
}
我使用循环将每一行数据放在正确的位置。数据不包括填充,但目标地址必须这样做。
因此我们需要将 数据访问 乘以实际 width * bytePerPixel
但 目标地址 乘以 Stride
,即扫描线的长度,填充到下一个四字节的倍数。对于 width=300
它是 stride=300
,对于 width=301
它是 stride=304
..
一步移动所有像素数据只有在没有填充时才有效,即当宽度是 4
的倍数时。