C#更新picturebox中的位图

Sla*_*shy 12 c# performance bitmap

我正在进行一个屏幕共享项目,我不断收到一小块图像Socket,需要在我拥有的某个初始dekstop位图上更新它们.

基本上我不断地从插座(其被存储为JPEG图像数据)读出的数据,使用Image.FromStream()以检索图像和复制收到块象素到全初级位图(在特定的位置XY其我也从插座获得) -这就是如何初始图像得到更新.但接下来是我需要在Picturebox 我处理Paint事件并再次重新绘制它的部分 - 整个初始图像,这是非常大的(在我的情况下是1920X1080).

这是我的代码:

    private void MainScreenThread()
    {
        ReadData();//reading data from socket.
        initial = bufferToJpeg();//first intial full screen image.
        pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
        while (true)
        {
            int pos = ReadData();
            x = BlockX();//where to draw :X
            y = BlockY();//where to draw :Y
            Bitmap block = bufferToJpeg();//constantly reciving blocks.
            Draw(block, new Point(x, y));//applying the changes-drawing the block on the big initial image.using native memcpy.

            this.Invoke(new Action(() =>
            {
                pictureBox1.Refresh();//updaing the picturebox for seeing results.
                // this.Text = ((pos / 1000).ToString() + "KB");
            }));
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        lock (initial)
        {
            e.Graphics.DrawImage(initial, pictureBox1.ClientRectangle); //draws at picturebox's bounds
        }
    }
Run Code Online (Sandbox Code Playgroud)

因为我的目标是高速性能(这是一种实时项目),我想知道是否没有任何方法可以在图片框上绘制当前收到的块本身,而不是再次绘制整个initial位图 -这对我来说效率非常低......这是我的绘图方法(工作极快,复制块memcpy):

     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;

            scan0 = IntPtr.Add(scan0, stride * Y + X * 3);//setting the pointer to the requested line
            for (int y = 0; y < Height; y++)
            {
                memcpy(scan0, scan02 ,(UIntPtr)(Width * 3));//copy one line

                scan02 = IntPtr.Add(scan02, stride2);//advance pointers
                scan0 = IntPtr.Add(scan0, stride);//advance pointers//
            }


            initial.UnlockBits(bmData);
            bmp2.UnlockBits(bmData2);
        }
    }
Run Code Online (Sandbox Code Playgroud)

以下是完整主要位图的一些示例,以及我正在获取并需要绘制完整位图的其他小块.

完整位图: 完整的位图 小块:

在此输入图像描述 小块:

在此输入图像描述

小块:

在此输入图像描述

我每秒获得大量的小块(30~40),其边界非常小(例如100X80像素的矩形),因此不需要重新绘制整个位图...快速刷新全屏图像会杀死表现......

我希望我的解释清楚.

期待一个答案.

谢谢.

Iva*_*oev 3

如果没有回答这个问题,那就太可惜了。在我的测试中,更新图片框的一小部分时,速度大约快了 10 倍。它的作用基本上是智能无效(考虑到缩放,仅使位图的更新部分无效)和智能绘画(仅绘制图片框的无效部分,取自e.ClipRectangle并考虑缩放):

private Rectangle GetViewRect() { return pictureBox1.ClientRectangle; }

private void MainScreenThread()
{
    ReadData();//reading data from socket.
    initial = bufferToJpeg();//first intial full screen image.
    pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
    // The update action
    Action<Rectangle> updateAction = imageRect =>
    {
        var viewRect = GetViewRect();
        var scaleX = (float)viewRect.Width / initial.Width;
        var scaleY = (float)viewRect.Height / initial.Height;
        // Make sure the target rectangle includes the new block
        var targetRect = Rectangle.FromLTRB(
            (int)Math.Truncate(imageRect.X * scaleX),
            (int)Math.Truncate(imageRect.Y * scaleY),
            (int)Math.Ceiling(imageRect.Right * scaleX),
            (int)Math.Ceiling(imageRect.Bottom * scaleY));
        pictureBox1.Invalidate(targetRect);
        pictureBox1.Update();
    };

    while (true)
    {
        int pos = ReadData();
        x = BlockX();//where to draw :X
        y = BlockY();//where to draw :Y
        Bitmap block = bufferToJpeg();//constantly reciving blocks.
        Draw(block, new Point(x, y));//applying the changes-drawing the block on the big initial image.using native memcpy.

        // Invoke the update action, passing the updated block rectangle
        this.Invoke(updateAction, new Rectangle(x, y, block.Width, block.Height));
    }
}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    lock (initial)
    {
        var viewRect = GetViewRect();
        var scaleX = (float)initial.Width / viewRect.Width;
        var scaleY = (float)initial.Height / viewRect.Height;
        var targetRect = e.ClipRectangle;
        var imageRect = new RectangleF(targetRect.X * scaleX, targetRect.Y * scaleY, targetRect.Width * scaleX, targetRect.Height * scaleY);
        e.Graphics.DrawImage(initial, targetRect, imageRect, GraphicsUnit.Pixel);
    }
}
Run Code Online (Sandbox Code Playgroud)

唯一棘手的部分是确定缩放的矩形,尤其是无效的矩形,因为需要浮点到整数的转换,所以我们确保它最终比需要的大一点,但不能小于所需的小。