倾斜位图,RGB565 C#的步幅计算

3
我的一些生成图像倾斜,而另一些则没有。

期望结果: (529x22)

enter image description here

实际结果: (529x22)

不要在意不同的图片大小,这些都是截图。它们都是529x22。

enter image description here

我正在使用的代码是从这里的一个答案中得到的。

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

但结果更倾斜:

输入图像描述


注:本文介绍了 IT 技术相关的内容。

2
你显然有一个步幅问题。如果你想得到帮助修复你的错误,请发布一个包含可靠重现问题的好[mcve]的问题,并更具体地描述在运行代码时出现的任何错误。很可能是因为你计算步幅的方式不正确;一些图像之所以能工作,是因为它们的宽度与你错误的计算兼容(Windows位图格式中的步幅会四舍五入到最近的32位...也许只有16位,我头脑中不能确定)。 - Peter Duniho
抱歉,我不知道它叫什么名字。对位图还不熟悉。我会尽力修正问题的。 - majidarif
是的,或者说:扫描线被填充到下一个四字节的倍数,也就是说 步幅 比净像素行多 0-3 个字节。 - TaW
你不需要计算步幅,也不需要去除填充,系统会自动计算步幅并插入填充。最有可能的错误是你一次性移动未填充的数据。循环遍历行似乎更有可能成功。 - TaW
让我们在聊天中继续这个讨论:点击此处进入聊天室 - TaW
显示剩余2条评论
2个回答

4

这似乎在这里起作用:

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的倍数时,一步移动所有像素数据才能起作用,即没有填充。

1
这里期望步长与宽度相对应,不考虑填充。但可以存在填充。填充会“吞噬”下一行的一部分,因此看起来会向左移动。
由于填充会打断行,除了使用相同的填充之外,唯一真正处理它的方法是逐行复制。您可以使用计算行的起始地址。从那里开始复制,对于每个y。

// bytes => 不使用此选项,因为它会导致错误

是的,因为您的数组没有填充。所以您告诉它要复制比数组容量更多的数据。

抱歉,我不明白你所说的逐行复制是什么意思。你能提供一个例子吗?谢谢。 - majidarif
@majidarif 逐行遍历,然后你可以按照自己的方式调用它。关键是,填充存在于图像的右侧,所以可以说那里有虚拟像素。它们应该被跳过,否则图像的一部分就会丢失在那里。 - harold
这是否意味着我需要创建一个新的字节数组,并将“pixels”复制到其中,跳过填充部分? - majidarif
你可以这样做,但是我是指直接将它分块地复制到位图中。使用临时数组作为额外步骤并不必要。 - harold
我按照你说的尝试了,但还是无法使其正常工作。 - majidarif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接