24bpp自定义Lockbits图像类仅宽度为4倍数时正常的bug排查求助
问题原因
你遇到的问题本质是Windows GDI+位图的行步长(Stride)对齐规则导致的:
- 24bppRgb格式的位图默认要求每一行的字节数必须对齐到4的倍数,不足的部分会在每行末尾填充1~3个无意义的占位字节
- 你当前的代码完全忽略了这个对齐规则,直接按
宽度*3计算每行的字节长度,只有当宽度*3刚好是4的倍数时,计算结果才和实际的行长度一致,这就是为什么只有宽度为4倍数时显示正常 - 当宽度不是4倍数时,每行实际长度比你计算的多了几个填充字节,每读取/写入一行你都会偏移几个字节的位置,越往下偏移量累计越大,就出现了底部内容右移、色彩失真的问题
修改方案
你需要调整三处核心逻辑:
- 锁定位图时读取
BitmapData.Stride属性获取实际每行字节数,不要硬编码计算 - 像素数组长度按
Stride * 高度计算,不要使用像素总数*3 - 像素索引计算规则改为
y * Stride + x * 3,替换原有的(y*宽度 +x)*3
修正后核心代码
Lock方法修改
private void Lock() { if (!locked) { Rectangle rect = new Rectangle(0, 0, Width, Height); bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); // 用实际步长计算总字节数,Abs兼容自下而上存储的位图 int totalBytes = Math.Abs(bitmapData.Stride) * Height; pixels = new byte[totalBytes]; Iptr = bitmapData.Scan0; Marshal.Copy(Iptr, pixels, 0, pixels.Length); locked = true; } }
GetPixel方法修改
public Color GetPixel(int x, int y) { if (!locked) Lock(); // 使用Stride计算像素起始索引 int i = y * bitmapData.Stride + x * 3; if (i + 2 > pixels.Length || i < 0) throw new IndexOutOfRangeException(); byte b = pixels[i]; byte g = pixels[i + 1]; byte r = pixels[i + 2]; return Color.FromArgb(r, g, b); }
SetPixel方法修改
public void SetPixel(int x, int y, Color color) { if (!locked) Lock(); // 使用Stride计算像素起始索引 int i = y * bitmapData.Stride + x * 3; if (i + 2 < pixels.Length && i >= 0) { pixels[i] = color.B; pixels[i + 1] = color.G; pixels[i + 2] = color.R; } }
Unlock方法不需要修改,其余逻辑保持原样即可。
内容的提问来源于stack exchange,提问作者aceofjohnonlone
相关产品推荐
相关产品推荐

