将字节数组复制到位图时,LockBits似乎导致错误的步幅 [英] LockBits seems to result in wrong stride when copying byte array to bitmap

查看:132
本文介绍了将字节数组复制到位图时,LockBits似乎导致错误的步幅的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将一个简单的字节数组复制到8位索引位图.使用许多论坛中无数已回答问题中所示的完全相同的代码,我仍然得到错误的结果.我尝试写入图像文件的数据为360字节,设置为18x20字节的线性数组.也就是说,前18个字节(0-17)属于图像的第一行,接下来的18个字节(18-35)属于第二行,依此类推.我已经确认该数据是正确的,我可以在Excel中手动解析(甚至通过设置单元格的背景色对其进行可视化).但是,当我尝试使用c#中的代码提取此内容时,会得到格式错误的图像.这是代码...

I'm trying to copy a simple byte array to an 8bit indexed bitmap. Using the exact same code as shown in countless answered questions on many forums, I still get the wrong result. The data I'm trying to write to image files is 360 bytes, setup as an 18x20 byte linear array. That is, the first 18 bytes (0-17) belong on the top row of the image, the next 18 bytes (18-35) belong on the 2nd row, etc. I have confirmed that this data is correct, as I can manually parse it in Excel (and even visualize it by setting the background color of cells). However, when I try to extract this using code in c#, I get a wrongly formatted image. Here is the code...

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之后,它对返回的位图没有影响.为什么会这样呢?请帮助,谢谢.

The result is as follows. The first row is written correctly. However, the starting with the second row, there is a 2 byte offset. That is, the first byte of the second row of the image ends up being byte #20 instead of byte #18 (starting from 0). Also, if I set a breakpoint immediately after the LockBits call, I can see that the bmpData has a "Stride" property equal to 20... even though the width is clearly set to 18. And, if I manually set the stride to 18, after the LockBits, it has no effect on the returned bitmap. Why is this happening? Please help, thanks.

推荐答案

您必须逐行复制它,将读取位置按图像数据中使用的跨度向前移动,将写入位置按BitmapData对象.

You have to copy it row by row, advancing your read position by the stride used in your image data, and your write position by the stride set in the BitmapData object.

在您的情况下,输入数据的跨度仅是宽度,但BitmapData's跨度与之不匹配,因为正如TaW所说,它总是四舍五入到下一个4字节的倍数.

In your case, the input data's stride is just the width, but the BitmapData's stride won't match that since, as TaW said, it's always rounded up to the next multiple of 4 bytes.

还请注意,这是一个8位图像,您需要添加调色板,否则它将最终得到标准的Windows调色板,该调色板可能根本与图像的预期颜色不匹配.

Also note, it being an 8-bit image, you need to add the palette, or it'll end up with a standard Windows palette that probably won't match your image's intended colours at all.

/// <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;
}

这篇关于将字节数组复制到位图时,LockBits似乎导致错误的步幅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆