C#更新picturebox中的位图

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

基本上我不断从套接字读取数据(数据存储为jpeg图像),使用Image.FromStream()检索图像并将接收到的块像素复制到完整的主位图(在特定位置XY ,我也得到从套接字) – 这是初始图像更新的方式。 但后来我需要在Picturebox上显示它的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 } } 

因为我的目标是高速性能(这是一种实时项目),我想知道是否没有任何方法可以在图片框上绘制当前收到的块本身,而不是再次绘制整个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); } } 

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

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

在此处输入图像描述 小块:

在此处输入图像描述

小块:

在此处输入图像描述

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

我希望我的解释清楚。

期待一个答案。

谢谢。

如果没有一些答案就离开这个问题会很遗憾。 在更新图片框的一小部分时,我的测试速度大约快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 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); } } 

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

如果您只需要在canvas上绘制,您只需绘制一次初始图像,然后使用CreateGraphics()DrawImage更新内容:

 ReadData(); initial = bufferToJpeg(); pictureBox1.Image = initial; var graphics = pictureBox1.CreateGraphics(); while (true) { int pos = ReadData(); Bitmap block = bufferToJpeg(); graphics.DrawImage(block, BlockX(), BlockY()); } 

我将通过性能比较来更新答案,因为我不相信这会带来任何重大好处; 它至少会避免双重DrawImage

请仔细阅读以下文件:

http://www.cs.columbia.edu/~lennox/draft-lennox-avt-app-sharing-00.html

我阅读它,它对桌面共享应用程序的理解有很大帮助。