我正在尝试将一个简单的字节数组复制到8位索引位图。使用与许多论坛上无数已回答问题中所示相同的代码,我仍然得到错误的结果。我尝试写入图像文件的数据为360字节,设置为18x20字节的线性数组。也就是说,前18个字节(0-17)属于图像的第一行,接下来的18个字节(18-35)属于第二行,依此类推。我已经确认该数据是正确的,我可以在Excel中手动解析它(甚至通过设置单元格的背景色对其进行可视化)。但是,当我尝试使用c#中的代码提取此内容时,会得到格式错误的图像。这是代码...
public Bitmap CopyByteDataToBitmap(byte[] byteData) {
Bitmap bmp = new Bitmap(18, 20, PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(byteData, 0, bmpData.Scan0, byteData.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
结果如下。第一行已正确写入。但是,从第二行开始,存在2个字节的偏移量。也就是说,图像第二行的第一个字节最终是字节#20而不是字节#18(从0开始)。另外,如果我在LockBits调用之后立即设置断点,则可以看到bmpData的“ Stride”属性等于20 ...即使宽度明确设置为18。并且,如果我手动将stride设置为18,在LockBits之后,它对返回的位图没有影响。为什么会这样呢?请帮助,谢谢。
答案 0 :(得分:0)
您必须逐行复制它,将读取位置按图像数据中使用的跨度向前移动,将写入位置按BitmapData
对象中设置的跨度向前移动。
在您的情况下,输入数据的跨度仅是宽度,但是BitmapData's
的跨度与之不符,因为正如TaW所说,它总是四舍五入到下一个4字节的倍数。
还请注意,这是一个8位图像,您需要添加调色板,否则它将最终得到标准的Windows调色板,该调色板可能根本与图像的预期颜色不匹配。
/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data.</param>
/// <param name="width">Width of the image.</param>
/// <param name="height">Height of the image.</param>
/// <param name="stride">Scanline length inside the data.</param>
/// <param name="pixelFormat">Pixel format.</param>
/// <param name="palette">Color palette.</param>
/// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
/// <returns>The new image.</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
for (Int32 y = 0; y < height; ++y)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
// For indexed images, set the palette.
if ((pixelFormat & PixelFormat.Indexed) != 0 && (palette != null || defaultColor.HasValue))
{
if (palette == null)
palette = new Color[0];
ColorPalette pal = newImage.Palette;
Int32 palLen = pal.Entries.Length;
Int32 paletteLength = palette.Length;
for (Int32 i = 0; i < palLen; ++i)
{
if (i < paletteLength)
pal.Entries[i] = palette[i];
else if (defaultColor.HasValue)
pal.Entries[i] = defaultColor.Value;
else
break;
}
// Palette property getter creates a copy, so the newly filled in palette is
// not actually referenced in the image until you set it again explicitly.
newImage.Palette = pal;
}
return newImage;
}