【问题标题】:C# Updating PictureBox imageC# 更新 PictureBox 图像
【发布时间】:2016-11-10 07:53:33
【问题描述】:

我正在做一个小屏幕共享项目,在主循环中(在第二个线程上)我从连接的套接字读取数据,将其处理成一个小图像(一种块,带有XY 属性指示在哪里绘制该块),并且需要在前一张图像上重新绘制它 - 它必须非常快速地更新(至少这是我的目标)。

到目前为止,我从初始图像创建了Graphics,并使用Graphics.DrawImage 方法在当前图像上绘制了较小的块(不断从套接字接收)。

看起来是这样的:

      private void MainScreenThread()
    {
        ReadData();
        initial = bufferToJpeg();
        while (true)
        {
            int pos = ReadData();
            x = BlockX();
            y = BlockY();
            Bitmap block = bufferToJpeg();
            Draw(block,new Point(x,y));
            this.Invoke(new Action(() => pictureBox1.Refresh()));         
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        lock (initial)
        {
            e.Graphics.DrawImage(initial, 0, 0);
        }
    }

我做了一些基准测试,我发现DrawImage 方法运行速度很慢。

绘制 800X500 图像的平均时间为 ~520ms~

我决定自己实现一个更快的方法,使用不安全的指针来更快地访问。

 private  unsafe void Draw(Bitmap bmp2, Point point)
    {
        lock (initial)
        {
            BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
            BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
            IntPtr scan0 = bmData.Scan0;
            IntPtr scan02 = bmData2.Scan0;
            int stride = bmData.Stride;
            int stride2 = bmData2.Stride;
            int Width = bmp2.Width;
            int Height = bmp2.Height;
            int X = point.X;
            int Y = point.Y;

            for (int y = 0; y < Height; y++)
            {
                byte* p = (byte*)scan0.ToPointer();
                p += (Y + y) * stride + X * 4;//setting pointer according the smaller bitmap bounds.
                byte* p2 = (byte*)scan02.ToPointer();
                p2 += stride2 * y;

                for (int x = 0; x < Width; x++)
                {
                    p[0] = p2[0];//B
                    p[1] = p2[1];//G
                    p[2] = p2[2];//R
                    p += 4;//advance pointer +4
                    p2 += 4;//advance pointer +4
                }

            }
            initial.UnlockBits(bmData);
            bmp2.UnlockBits(bmData2);
        }
    }

现在调用看起来像这样:

   while (true)
        {
            int pos = ReadData();
             x = BlockX();
            y = BlockY();
            Bitmap block = bufferToJpeg();
           Draw(block,new Point(x,y));
            this.Invoke(new Action(() => pictureBox1.Refresh()));         
        }

但我一直收到一个

对象当前在别处使用

当我锁定初始图像时..在这一行

 BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);

我不确定为什么会发生这种情况..我没有从任何其他来源访问 initial...

我将不胜感激。

谢谢。

【问题讨论】:

  • 加锁的时候,能不能先用这个语句:lock(initial)lock(bmp2)

标签: c# multithreading sockets thread-safety


【解决方案1】:

您正在从另一个来源访问 initial:即由于您的 pictureBox1.Image = initial 而负责绘制图片框的主 GUI 线程

我的建议。摆脱pictureBox1.Image = initial 位,并在pictureBox 中添加一个OnPaint 处理程序以手动绘制图像。然后围绕资源的使用添加一些简单的锁定代码,例如在您的后台线程中,例如:

lock (initial)
{
    BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
    // ....
    initial.UnlockBits(bmData);
}

然后在您的 OnPaint 处理程序中,执行以下操作:

lock (initial)
{
    e.Graphics.DrawImage(initial, 0, 0);
}

这将防止两个线程在尝试访问同一资源时发生冲突。

【讨论】:

  • 首先感谢您的回答。我已根据您的建议更新了代码,但现在我在绘制事件(当我锁定初始图像时)收到 Object cannot be null 异常。在这一行lock (initial)。请参阅更新后的问题。
  • 哦!我修好了..最初的初始真的是空的,哈哈。还有一件事,因为这个孩子或程序真的需要效率,这是在图片框中再次绘制它的最快方法吗?分配像PictureBox1.Image=initial; 这样的值不会更快?
  • 有人仍然需要绘制图像,没有理由认为在一个地方绘制它会比另一个地方快得多。事实上,当您设置 image 属性时,它仍然通过 Graphics.DrawImage 调用绘制,正如您在 source to PictureBox 中看到的那样。
  • 好的。惊人的 :)。最后一个想法-尽管图片框处于拉伸模式,但它仍然看起来不像这样......有机会通过Paint 事件更改它吗?在我使用您的解决方案之前,它确实以拉伸模式显示图像:)
  • DrawImage 调用中添加一个矩形,如e.Graphics.DrawImage(initial, pictureBox1.ClientRectangle); 以拉伸图像。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 2012-07-14
  • 1970-01-01
相关资源
最近更新 更多