使用 LockBits/Marshal.Copy 从 S.D.Bitmap 转换为 MonoGame Texture2D - 结果失真

Using LockBits/Marshal.Copy to convert from S.D.Bitmap to MonoGame Texture2D - Result is distorted

我正在尝试编写一个接受 System.Drawing.Bitmap 并尽快生成 Monogame Texture2D 的函数。

我已经设法在位图上使用 GetPixel 使其工作,这很简单,但是 far 当然太慢了。

我仔细研究并发现了一种使用 LockBits 和 Marshal.Copy 快速访问像素值的更快的技术。问题是,它在某些图像上完美运行,而其他图像最终看起来像这样: Example Image

据我所知,这与未正确补偿 Stride 有关,但我不熟悉在这个级别直接处理图像数据,我完全迷路了。我在这里做错了什么导致这种灰度倾斜的结果?

当这对大多数图像有效时,它非常快并且正是我想要的。如果重要的话,我将专门处理网络上非常典型的 JPG/PNG 图片。

public static Texture2D FromBitmap(MagickImage input, GraphicsDevice graphicsDevice)
        {
            var bounds = new Rectangle(0, 0, input.Width, input.Height);

            var bitmap = input.ToBitmap();

            var output = new Texture2D(graphicsDevice, bitmap.Width, bitmap.Height);

            var outputArray = new Color[bitmap.Width * bitmap.Height];

            var bitmapBits = bitmap.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            var bitmapDepth = Image.GetPixelFormatSize(bitmapBits.PixelFormat) / 8;
            var bitmapBuffer = new byte[bitmapBits.Stride * bitmapBits.Height];

            Marshal.Copy(bitmapBits.Scan0, bitmapBuffer, 0, bitmapBuffer.Length);

            for (var y = 0; y < bitmap.Height; y++)
            {
                for (var x = 0; x < bitmap.Width; x++)
                {
                    var arrayPoint = x + (y * bitmap.Width);

                    outputArray[arrayPoint].R = bitmapBuffer[(arrayPoint * bitmapDepth) + 2];
                    outputArray[arrayPoint].G = bitmapBuffer[(arrayPoint * bitmapDepth) + 1];
                    outputArray[arrayPoint].B = bitmapBuffer[(arrayPoint * bitmapDepth) + 0];
                }
            }

            output.SetData(outputArray);

            return output;
        }
    }

编辑:我在指向 outputArray 中的 arrayPoint 时犯了一个简单的错误,这是上次尝试的遗留问题。更正此 + 重写 arrayPoint 定义修复了它。以下作品:

var arrayPoint = (y * bitmapBits.Stride) + (x * bitmapDepth);

outputArray[(y * bitmap.Width) + x].R = bitmapBuffer[arrayPoint + 2];
outputArray[(y * bitmap.Width) + x].G = bitmapBuffer[arrayPoint + 1];
outputArray[(y * bitmap.Width) + x].B = bitmapBuffer[arrayPoint + 0];

您忽略了 stride,这意味着有些图片可以使用,有些则不能。

步幅不是 bitmap.Width * 位图深度。
步幅是单行像素(扫描线)的宽度,四舍五入到四字节边界。

所以你总是必须为每一行增加你的 arrayPoint,而不是一些手动计算。