首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LockBits性能临界码

LockBits性能临界码
EN

Stack Overflow用户
提问于 2009-04-11 18:10:31
回答 10查看 6.1K关注 0票数 3

我有一个需要尽可能快的方法,它使用不安全的内存指针,这是我第一次尝试这种类型的编码,所以我知道它可能会更快。

代码语言:javascript
复制
    /// <summary>
    /// Copies bitmapdata from one bitmap to another at a specified point on the output bitmapdata
    /// </summary>
    /// <param name="sourcebtmpdata">The sourcebitmap must be smaller that the destbitmap</param>
    /// <param name="destbtmpdata"></param>
    /// <param name="point">The point on the destination bitmap to draw at</param>
    private static unsafe void CopyBitmapToDest(BitmapData sourcebtmpdata, BitmapData destbtmpdata, Point point)
    {
        // calculate total number of rows to draw.
        var totalRow = Math.Min(
            destbtmpdata.Height - point.Y,
            sourcebtmpdata.Height);


        //loop through each row on the source bitmap and get mem pointers
        //to the source bitmap and dest bitmap
        for (int i = 0; i < totalRow; i++)
        {
            int destRow = point.Y + i;

            //get the pointer to the start of the current pixel "row" on the output image
            byte* destRowPtr = (byte*)destbtmpdata.Scan0 + (destRow * destbtmpdata.Stride);
            //get the pointer to the start of the FIRST pixel row on the source image
            byte* srcRowPtr = (byte*)sourcebtmpdata.Scan0 + (i * sourcebtmpdata.Stride);

            int pointX = point.X;
            //the rowSize is pre-computed before the loop to improve performance
            int rowSize = Math.Min(destbtmpdata.Width - pointX, sourcebtmpdata.Width);
            //for each row each set each pixel
            for (int j = 0; j < rowSize; j++)
            {
                int firstBlueByte = ((pointX + j)*3);

                int srcByte = j *3;
                destRowPtr[(firstBlueByte)] = srcRowPtr[srcByte];
                destRowPtr[(firstBlueByte) + 1] = srcRowPtr[srcByte + 1];
                destRowPtr[(firstBlueByte) + 2] = srcRowPtr[srcByte + 2];
            }


        }
    }

有什么办法可以让这件事更快吗?现在忽略这个任务,等我有了一些基线性能度量后,我会修正这个问题。

更新:对不起,应该提到我使用这个而不是Graphics.DrawImage的原因是因为我实现了多线程,因为我不能使用DrawImage。

更新2:,我仍然对性能不满意,我相信还有几个ms可以使用。

EN

回答 10

Stack Overflow用户

回答已采纳

发布于 2009-05-13 15:50:25

代码有一些根本的错误,我不敢相信我直到现在才注意到。

代码语言:javascript
复制
byte* destRowPtr = (byte*)destbtmpdata.Scan0 + (destRow * destbtmpdata.Stride);

这将获得一个指向目标行的指针,但它不会得到要复制到的列,在旧代码中,这是在rowSize循环中完成的。现在看来:

代码语言:javascript
复制
byte* destRowPtr = (byte*)destbtmpdata.Scan0 + (destRow * destbtmpdata.Stride) + pointX * 3;

所以现在我们有了目标数据的正确指针。现在我们可以去掉这个循环了。使用维尔克斯-抢夺的建议,代码现在如下所示:

代码语言:javascript
复制
        private static unsafe void CopyBitmapToDestSuperFast(BitmapData sourcebtmpdata, BitmapData destbtmpdata, Point point)
    {
        //calculate total number of rows to copy.
        //using ternary operator instead of Math.Min, few ms faster
        int totalRows = (destbtmpdata.Height - point.Y < sourcebtmpdata.Height) ? destbtmpdata.Height - point.Y : sourcebtmpdata.Height;
        //calculate the width of the image to draw, this cuts off the image
        //if it goes past the width of the destination image
        int rowWidth = (destbtmpdata.Width - point.X < sourcebtmpdata.Width) ? destbtmpdata.Width - point.X : sourcebtmpdata.Width;

        //loop through each row on the source bitmap and get mem pointers
        //to the source bitmap and dest bitmap
        for (int i = 0; i < totalRows; i++)
        {
            int destRow = point.Y + i;

            //get the pointer to the start of the current pixel "row" and column on the output image
            byte* destRowPtr = (byte*)destbtmpdata.Scan0 + (destRow * destbtmpdata.Stride) + point.X * 3;

            //get the pointer to the start of the FIRST pixel row on the source image
            byte* srcRowPtr = (byte*)sourcebtmpdata.Scan0 + (i * sourcebtmpdata.Stride);

            //RtlMoveMemory function
            CopyMemory(new IntPtr(destRowPtr), new IntPtr(srcRowPtr), (uint)rowWidth * 3);

        }
    }

在网格中将500x500图像复制到5000x5000图像需要50次: 00:00:07.9948993秒。现在,随着以上的变化,它需要00:00:01.8714263秒。好多了。

票数 4
EN

Stack Overflow用户

发布于 2009-04-11 18:13:56

好吧..。我不确定.NET位图数据格式是否与GDI32函数完全兼容.

但是,我学到的最初几个Win32 API之一是BitBlt:

代码语言:javascript
复制
BOOL BitBlt(
  HDC hdcDest, 
  int nXDest, 
  int nYDest, 
  int nWidth, 
  int nHeight, 
  HDC hdcSrc, 
  int nXSrc, 
  int nYSrc, 
  DWORD dwRop
);

如果我没记错的话,这是最快的复制数据的方式。

下面是BitBlt PInvoke签名,用于C#和相关的使用信息,对于任何在C#中使用高性能图形的人来说都是一个很好的读物:

  • http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html

绝对值得一看。

票数 2
EN

Stack Overflow用户

发布于 2009-04-11 22:51:21

内环是你想要集中大量时间的地方(但是,要做测量以确保)

代码语言:javascript
复制
for  (int j = 0; j < sourcebtmpdata.Width; j++)
{
    destRowPtr[(point.X + j) * 3] = srcRowPtr[j * 3];
    destRowPtr[((point.X + j) * 3) + 1] = srcRowPtr[(j * 3) + 1];
    destRowPtr[((point.X + j) * 3) + 2] = srcRowPtr[(j * 3) + 2];
}
  1. 摆脱乘法和数组索引(这是一个乘数下的头罩),并替换为一个指针,您正在递增。
  2. 与+1,+2相同,增加指针。
  3. 您的编译器可能不会继续计算Pointt.x(检查),但会生成一个局部变量,以防万一。它不会在一次迭代中完成,但每次迭代都可能这样做。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/740555

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档