【问题标题】:Copy one Bitmap onto a larger Bitmap using without using Graphics.DrawImage使用不使用 Graphics.DrawImage 将一个位图复制到一个更大的位图上
【发布时间】:2010-10-13 23:36:42
【问题描述】:

这是Rendering to a single Bitmap object from multiple threads的后续跟进

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

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

【问题讨论】:

  • 跟进?我不只是重复同样的问题。
  • 这是一个更集中的问题,而且我得到的答案也更集中。

标签: c# graphics drawing bitmap unmanaged


【解决方案1】:

您可以在不依赖任何系统调用的情况下操作内存中的图像。如果您深入研究 .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 最烦人的挑战是:

  1. 将 DIB 转换为实际的“图像”以供显示和
  2. 将实际的“图像”转换为 DIB
  3. 将 DIB 保存为 .BMP 以外的其他内容

当我开始了解图像实际上是“恐怖”时的核心参考:

您如何访问/从 .NET Image 获取...嗯...这是一个好问题 :)

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我建议你看看the internal bitmap memory structure.

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

      当您合成更大的图像时,您可以从字节数据中获取最终的字节数组并直接make a Bitmap

      这具有允许您控制内存管理、线程操作等的优势,就像您在原始帖子中似乎想要做的那样。数据访问也应该非常快。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-09
        相关资源
        最近更新 更多