使用不使用Graphics.DrawImage将一个位图复制到更大的位图上

时间:2009-03-18 00:46:17

标签: c# graphics drawing bitmap unmanaged

这是Rendering to a single Bitmap object from multiple threads

的后续行动

我想要实现的是采用50x50像素的位图,并使用位图LockBits函数或任何其他但不是graphics.DrawImage将其绘制到较大图像上任意点的较大位图(100x100像素)上。我不想使用DrawImage的原因在另一个帖子中说明。

我已经设法通过使用Marshal.Copy从源BitmapData到dest BitmapData获得了一些东西,但是它水平地创建了一个平铺的拉伸图像。

3 个答案:

答案 0 :(得分:4)

您可以在不依赖任何系统调用的情况下操作内存中的图像。如果你深入研究.BMP文件的基础格式,你可以构建自己的Device Independant Bitmap类,它真正“理解”.BMP的低级格式。

例如,每像素8位图像本质上是2维字节数组(每个字节是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时最烦人的挑战是:

  1. 将DIB转换为准备显示的实际“图像”和
  2. 将实际“图像”转换为DIB
  3. 将DIB保存为.BMP以外的其他内容
  4. 当我开始学习图像实际上的“恐怖”时的核心参考:

    你如何进出.NET Image的......好吧......这是一个很好的问题:)

答案 1 :(得分:1)

如果您使用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的步幅可能包括一些不应复制的填充。您可以通过计算源行中实际像素数据的数量来解决此问题,并复制此数量。索引像素格式将更加有用。

答案 2 :(得分:0)

我建议您查看the internal bitmap memory structure.

我认为最好的方法是不要尝试直接设置BitmapData。相反,我会创建一个适当大小的单个共享字节数组,并直接从较小的图像设置字节数组。

构成较大的图像后,可以从字节数据中直接获取最终的字节数组{/ 3}}。

这样做的好处是可以控制内存管理,操作操作等,就像您在原始帖子中想要做的那样。它也应该非常快速地进行数据访问。