这里有很多答案,但都非常糟糕:(
绝对最好的方法是创建一个位图并将一个 intptr(指针)传递给一个现有数组。这允许数组和位图数据共享相同的内存......无需'Bitmap.lockbits'/'Bitmap.unlockbits'(慢)。
这是大致轮廓...
- 将您的函数标记为“不安全”并将您的项目构建设置设置为允许“不安全”代码! (c# 指针)
- 创建您的数组[,]。使用 Uint32、字节或允许 Uint32 或单个 Uint8 访问的结构 (通过使用显式字段偏移)
- 使用“System.Runtime.InteropServices.Marshal.UnsaveAddrOfPinnedArrayElement”获取数组开头的 Intptr。
- 使用采用 Intptr 和 Stride 的构造函数创建位图。这将使新位图与现有数组数据重叠。
您现在可以永久直接访问像素数据!
底层数组
底层数组可能是用户结构“像素”的二维数组。为什么?嗯...结构可以允许多个成员变量通过使用显式的固定偏移来共享相同的空间!这意味着该结构可以有 4 个单字节成员(.R、.G、.B 和 .A),以及 3 个重叠的 Uint16(.AR、.RG 和 ,GB)……和一个 Uint32(.ARGB ) ...这可以使色彩平面操作更快。
由于 R、G、B、AR、RG、GB 和 ARGB 都访问同一个 32 位像素的不同部分,您可以以高度灵活的方式操作像素!
因为 Pixel[,] 的数组与 Bitmap 本身共享相同的内存,所以图形操作会立即更新 Pixel 数组 - 而对数组的 Pixel[,] 操作会立即更新位图!您现在有多种方式来操作位图 ;)
灵活且快速 ;)
请记住,通过使用这种技术,您不需要使用“lockbits”来将位图数据编组进出缓冲区……这很好,因为 lockbits 非常非常慢。
您也不需要使用画笔并调用能够绘制图案、可缩放、可旋转、可平移、可别名、矩形的复杂框架代码......只需编写一个像素相信我 - 图形中的所有灵活性类使得使用“Graphics.FillRect”绘制单个像素的过程非常缓慢。
其他好处...
超级流畅的滚动!您的像素缓冲区在高度和宽度上都可以大于您的画布/位图!这可以实现有效的滚动!!!!
怎么做?
好吧,当您从数组创建位图时,您可以通过获取该 Pixel[,] 的 IntPtr 将位图的左上角坐标指向任意 [y,x] 坐标。
然后,通过故意设置位图“步幅”以匹配数组的宽度(不是位图的宽度),您可以渲染较大数组的预定义子集矩形......同时绘制(提前)进入看不见的边缘!这就是平滑滚动条中“离屏绘制”的原理。
终于
您真的应该将 Bitmap 和 Array 包装到 FastBitmap 类中。这将帮助您控制数组/位图对的生命周期。显然,如果数组超出范围或被破坏 - 位图将指向非法内存地址。通过将它们包装在 FastBitmap 类中,您可以确保不会发生这种情况......
...它也是一个非常方便的地方,可以放置您不可避免地想要添加的各种实用程序...例如滚动、淡入淡出、使用颜色平面等。
记住:
- 从 MemoryStream 创建位图非常慢
- 使用 Graphics.FillRect 绘制像素效率极低
- 使用 lockpixels/unlockpixels 访问底层位图数据非常慢
- 而且,如果您使用的是“System.Runtime.InteropServices.Marshal.Copy”,请停止!
将位图映射到一些现有的数组内存是可行的方法。做对了,你就再也不需要/想再次使用框架位图了:)
不客气;)