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

发布于 2024-07-15 18:41:03 字数 363 浏览 5 评论 0 原文

这是从多个渲染到单个位图对象的后续内容 我想要实现的目标是使用位图 LockBits 函数或任何其他但不是图形的函数,

将 50x50 像素的位图绘制到较大图像上的任何点上的较大位图(100x100 像素)上。绘制图像。 我不想使用 DrawImage 的原因在另一个线程中有说明。

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

This is a follow up from Rendering to a single Bitmap object from multiple threads

What im trying to achieve is to take a bitmap of say 50x50 pixels and draw it onto a larger bitmap(100x100 pixels) at any point on the larger image, using the bitmaps LockBits function or any other but NOT graphics.DrawImage. My reasons for not wanting to use DrawImage is stated in the other thread.

I have managed to get something by using Marshal.Copy from the source BitmapData to the dest BitmapData but its creating a tiled, stretched image horizontally.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半寸时光 2024-07-22 18:41:03

您可以在不依赖任何系统调用的情况下操作内存中的图像。 如果您深入研究 .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. .BMP 核心参考”时,将 DIB 保存为 .BMP 核心参考之外的其他内容

当我开始学习“ 恐怖”,图像实际上是:

你如何进入/来自 .NET Image's...嗯...这是一个很好的问题:)

You could manipulate the image in memory without relying on any system calls. If you dig into the underlying format of a .BMP file you could build your own Device Independant Bitmap class that truly "understands" the low level format of a .BMP.

For example a 8 bit per pixel image is essentially a 2 dimensional array of bytes (each byte is 1 pixel) plus a simple color table. Roughly speaking (and this is very very rough):

byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels 

The trick is (as always) getting a hold of the raw pixel data, doing the processing, and then updating the raw pixel data with your changes.

In the past I've approached this by converting a GDI HBITMAP into a 24bpp DIB, doing my funky image processing on the raw pixels (3 bytes per pixels makes this easier), then converting the DIB back into a HBITMAP. This was all using just classic GDI (pre GDI+ even, let alone C#).

Using that approach you could design a control structure to allow multiple writers to different sections of your much bigger image.

However... the lowlevel BitBlt GDI calls are likely to be way more efficient that anything you can do. If I were you I'd make certain that just doing 50 or 100 bitblt's in a row would be too slow (you'd likely need to do this in c++).

The most annoying challenges with dealing with DIB's are:

  1. Converting a DIB to an actual "image" ready for display and
  2. Converting an actual "image" into a DIB
  3. Saving a DIB as something other than a .BMP

Core references when I started learning the "horror" that images actually are:

How you go about getting to/from .NET Image's... well... that's a good question :)

半暖夏伤 2024-07-22 18:41:03

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

This should work just fine using LockBits/BitmapData, if you are using a 32bpp [P]ARGB pixel format. The trick is that you will have to copy the data one row at a time so that it aligns in the correct places. You should be able to do this using something like:

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);

As a warning, this code won't work as is because I am not sure what the right incantations are for incrementing IntPtr's. I've done this same type of thing before, but in C++. Also, I don't know if there is a way to directly copy the data instead of using an intermediate buffer.

An additional caveat: the LockBits call srcBitmap and the sizing of the buffer assume that srcBitmap will be completely enclosed in destBitmap. If this is not the case (some part of the bitmap will be cropped off) the area locked and the size of the buffer will need to be adjusted.

If you are not using a 32bpp pixel format (ie 24bpp), it will be more difficult. The stride of your source BitmapData may include some amount of padding that should not be copied. You could work around this by calculating the amount of actual pixel data in a source row, and copy this amount. Indexed pixel formats would be even more work.

情魔剑神 2024-07-22 18:41:03

我建议您查看 内部位图内存结构。

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

当您组合较大的图像时,您可以获取最终的字节数组并直接 制作一个来自字节数据的位图

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

I would recommend taking a look at the internal bitmap memory structure.

The best approach, I think, would be to not try to set the BitmapData directly. Instead, I would make a single, shared byte array of the appropriate size, and set the byte array directly from your smaller images.

When you compose your larger image, you can take the final byte array and directly make a Bitmap from the byte data.

This has the advantage of allowing you to control the memory management, thread the operations, etc, as you seemed to want to do in your original post. It should be very fast for the data access, as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文