这是从多个线程渲染到单个位图对象的后续内容
我试图实现的是使用位图 LockBits 函数或任何其他但不是 graphics.DrawImage 的位图,将其绘制到更大图像上任何点的 50x50 像素位图并绘制到更大的位图(100x100 像素)上。我不想使用 DrawImage 的原因在另一个线程中说明。
我已经设法通过使用 Marshal.Copy 从源 BitmapData 到 dest BitmapData 得到一些东西,但它创建了一个平铺的水平拉伸图像。
您可以在不依赖任何系统调用的情况下操作内存中的图像。如果您深入研究 .BMP 文件的底层格式,您可以构建自己的设备独立位图类,该类真正“理解”.BMP 的低级格式。
例如,每像素 8 位的图像本质上是一个二维字节数组(每个字节为 1 个像素)加上一个简单的颜色表。粗略地说(这是非常非常粗略的):
byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels
诀窍是(一如既往)获取原始像素数据,进行处理,然后根据您的更改更新原始像素数据。
过去,我通过将 GDI HBITMAP 转换为 24bpp DIB 来解决此问题,对原始像素进行时髦的图像处理(每像素 3 个字节使这更容易),然后将 DIB 转换回 HBITMAP。这一切都只使用了经典的 GDI(甚至是 GDI+ 之前的版本,更不用说 C#了)。
使用这种方法,您可以设计一个控制结构,以允许多个作者访问您更大图像的不同部分。
但是...低级 BitBlt GDI 调用可能比您可以做的任何事情都更有效率。如果我是你,我会确定连续执行 50 或 100 个 bitblt 会太慢(你可能需要在 c++ 中执行此操作)。
处理 DIB 最烦人的挑战是:
当我开始学习图像实际上是“恐怖”时的核心参考:
你如何去/从 .NET Image 的......嗯......这是一个很好的问题 :)
如果您使用的是 32bpp [P]ARGB 像素格式,则使用 LockBits/BitmapData 应该可以正常工作。诀窍是您必须一次复制一行数据,以便它在正确的位置对齐。您应该可以使用以下方法执行此操作:
Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat);
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat);
IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride];
for (int i = 0; i < srcData.Height; ++i)
{
Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
Marshal.Copy(buffer, 0, destPtr, buffer.Length);
srcPtr += srcData.Stride;
destPtr += destData.Stride;
}
srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);
作为警告,此代码不会按原样工作,因为我不确定增加 IntPtr 的正确咒语是什么。我以前做过同样类型的事情,但在 C++ 中。另外,我不知道是否有办法直接复制数据而不是使用中间缓冲区。
另一个警告:LockBits 调用 srcBitmap 和缓冲区大小假定 srcBitmap 将完全包含在 destBitmap 中。如果不是这种情况(位图的某些部分将被裁剪掉),则需要调整锁定区域和缓冲区的大小。
如果不使用 32bpp 像素格式(即 24bpp),会比较困难。源 BitmapData 的步幅可能包括一些不应复制的填充。您可以通过计算源行中的实际像素数据量来解决此问题,然后复制该数量。索引像素格式将更加有效。