You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用字节数组填充PictureBox出现异常效果的问题排查

问题分析与解决方案

你的问题出在1bpp索引位图的扫描线对齐机制上——Windows系统要求位图的扫描线(每行像素的内存长度)必须按4字节对齐,而你直接把字节数组整体复制到位图内存时,没有考虑这个对齐带来的填充字节,导致内存写入时出现覆盖问题。

具体原因

对于你创建的48x32、1bpp格式的位图:

  • 每行的有效像素字节数是 (48 + 7) / 8 = 6(因为1字节对应8个像素)
  • 但系统会把扫描线长度对齐到4的倍数,所以实际每行占用的内存(即bmpData.Stride)是 ((48*1 + 31)/32)*4 = 8 字节——这意味着每行有2字节的填充数据,不对应任何像素。

当你直接用Marshal.Copy把整个fileArray复制到Scan0时,字节数组里的后续内容会覆盖到前面行的填充字节区域,甚至可能因为内存地址重叠,破坏已经写入的有效像素数据(就像你遇到的126字节覆盖125字节的情况)。

修复后的代码

正确的做法是逐行复制有效像素数据,跳过每行末尾的填充字节:

var bmp = new Bitmap(48, 32, PixelFormat.Format1bppIndexed);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);

int bytesPerRow = (bmp.Width + 7) / 8; // 计算每行的有效像素字节数(48像素=6字节)
int stride = bmpData.Stride; // 获取系统对齐后的实际每行内存长度(这里是8字节)

// 逐行写入数据
for (int y = 0; y < bmp.Height; y++)
{
    // 计算当前行在fileArray中的起始索引
    int sourceStart = y * bytesPerRow;
    // 计算当前行在位图内存中的起始地址
    IntPtr rowAddress = IntPtr.Add(bmpData.Scan0, y * stride);
    
    // 只复制当前行的有效像素字节,避开填充区域
    Marshal.Copy(fileArray, sourceStart, rowAddress, bytesPerRow);
}

bmp.UnlockBits(bmpData);
return bmp;

额外说明

  • 1bpp位图的每个字节中,最高位对应该行最左侧的像素,最低位对应最右侧的像素,如果你的像素顺序不对,可能需要调整每个字节的位顺序。
  • 确保fileArray的长度至少是 bytesPerRow * bmp.Height(即6*32=192字节),避免索引越界。

内容的提问来源于stack exchange,提问作者sean green

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:07:20