-1

我正在尝试将一个简单的字节数组复制到一个 8 位索引位图。使用与许多论坛上无数回答问题中显示的完全相同的代码,我仍然得到错误的结果。我试图写入图像文件的数据是 360 字节,设置为 18x20 字节线性数组。也就是说,前 18 个字节(0-17)属于图像的顶行,接下来的 18 个字节(18-35)属于第 2 行,依此类推。我已经确认这个数据是正确的,我可以在 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 的“步幅”属性等于 20...即使宽度明确设置为 18。而且,如果我手动将步幅设置为18、LockBits后,对返回的位图没有影响。为什么会这样?请帮忙,谢谢。

4

1 回答 1

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;
}
于 2019-07-24T20:27:22.043 回答