C# Update bitmap in picturebox

后端 未结 3 1371
再見小時候
再見小時候 2021-02-19 18:17

I\'m working on a screen sharing project ,and i recieve a small blocks of image from a Socket constantly and need to update them on a certain initial dekstop bitmap

相关标签:
3条回答
  • 2021-02-19 18:41

    If you just need to draw on top of the canvas, you can draw the initial image just once and then use CreateGraphics() and DrawImage to update the content:

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

    I'll update the answer with a performance comparison as I'm not convinced this will give any major benefit; it will, at least, avoid a double DrawImage though.

    0 讨论(0)
  • 2021-02-19 18:43

    It would be shame to leave that question without some answer. The following is about 10 times faster in my tests when updating small portions of the picture box. What it does basically is smart invalidating (invalidates just the updated portion of the bitmap, considering the scaling) and smart painting (draws only the invalidated portion of the picture box, taken from e.ClipRectangle and considering the scaling):

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

    The only kind of tricky part is determining the scaled rectangles, especially the one for invalidating, due to floating point to int conversions required, so we make sure it's eventually a little bigger than needed, but not less.

    0 讨论(0)
  • 2021-02-19 18:56

    Please carefully read the following document:

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

    I read it and it is helping a lot with the understanding of desktop - sharing application.

    0 讨论(0)
提交回复
热议问题